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

uber / h3-java / #490

29 May 2025 05:20PM UTC coverage: 94.382% (-2.3%) from 96.673%
#490

push

github

web-flow
Convert build system to Gradle (#167)

* gradle

* wip

* wip

* rewrite website

* ci

* no spotless

* run build before test

* wip

* coveralls with newer JDK

* fix test

* add nexus

* switch coveralls plugin

* turn off config cache

154 of 171 branches covered (90.06%)

Branch coverage included in aggregate %.

518 of 541 relevant lines covered (95.75%)

0.96 hits per line

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

94.58
/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 index. */
107
  public boolean isValidCell(long h3) {
108
    return h3Api.isValidCell(h3);
1✔
109
  }
110

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

116
  /** Returns the base cell number for this index. */
117
  public int getBaseCellNumber(long h3) {
118
    return h3Api.getBaseCellNumber(h3);
1✔
119
  }
120

121
  /** Returns the base cell number for this index. */
122
  public int getBaseCellNumber(String h3Address) {
123
    return getBaseCellNumber(stringToH3(h3Address));
1✔
124
  }
125

126
  /** Returns <code>true</code> if this index is one of twelve pentagons per resolution. */
127
  public boolean isPentagon(long h3) {
128
    return h3Api.isPentagon(h3);
1✔
129
  }
130

131
  /** Returns <code>true</code> if this index is one of twelve pentagons per resolution. */
132
  public boolean isPentagon(String h3Address) {
133
    return isPentagon(stringToH3(h3Address));
1✔
134
  }
135

136
  /**
137
   * Find the H3 index of the resolution <code>res</code> cell containing the lat/lon (in degrees)
138
   *
139
   * @param lat Latitude in degrees.
140
   * @param lng Longitude in degrees.
141
   * @param res Resolution, 0 &lt;= res &lt;= 15
142
   * @return The H3 index.
143
   */
144
  public long latLngToCell(double lat, double lng, int res) {
145
    checkResolution(res);
1✔
146
    return h3Api.latLngToCell(toRadians(lat), toRadians(lng), res);
1✔
147
  }
148

149
  /**
150
   * Find the H3 index of the resolution <code>res</code> cell containing the lat/lon (in degrees)
151
   *
152
   * @param lat Latitude in degrees.
153
   * @param lng Longitude in degrees.
154
   * @param res Resolution, 0 &lt;= res &lt;= 15
155
   * @return The H3 index.
156
   */
157
  public String latLngToCellAddress(double lat, double lng, int res) {
158
    return h3ToString(latLngToCell(lat, lng, res));
1✔
159
  }
160

161
  /** Find the latitude, longitude (both in degrees) center point of the cell. */
162
  public LatLng cellToLatLng(long h3) {
163
    double[] coords = new double[2];
1✔
164
    h3Api.cellToLatLng(h3, coords);
1✔
165
    LatLng out = new LatLng(toDegrees(coords[0]), toDegrees(coords[1]));
1✔
166
    return out;
1✔
167
  }
168

169
  /** Find the latitude, longitude (degrees) center point of the cell. */
170
  public LatLng cellToLatLng(String h3Address) {
171
    return cellToLatLng(stringToH3(h3Address));
1✔
172
  }
173

174
  /** Find the cell boundary in latitude, longitude (degrees) coordinates for the cell */
175
  public List<LatLng> cellToBoundary(long h3) {
176
    double[] verts = new double[MAX_CELL_BNDRY_VERTS * 2];
1✔
177
    int numVerts = h3Api.cellToBoundary(h3, verts);
1✔
178
    List<LatLng> out = new ArrayList<>(numVerts);
1✔
179
    for (int i = 0; i < numVerts; i++) {
1✔
180
      LatLng coord = new LatLng(toDegrees(verts[i * 2]), toDegrees(verts[(i * 2) + 1]));
1✔
181
      out.add(coord);
1✔
182
    }
183
    return out;
1✔
184
  }
185

186
  /** Find the cell boundary in latitude, longitude (degrees) coordinates for the cell */
187
  public List<LatLng> cellToBoundary(String h3Address) {
188
    return cellToBoundary(stringToH3(h3Address));
1✔
189
  }
190

191
  /**
192
   * Neighboring indexes in all directions.
193
   *
194
   * @param h3Address Origin index
195
   * @param k Number of rings around the origin
196
   */
197
  public List<String> gridDisk(String h3Address, int k) {
198
    return h3ToStringList(gridDisk(stringToH3(h3Address), k));
1✔
199
  }
200

201
  /**
202
   * Neighboring indexes in all directions.
203
   *
204
   * @param h3 Origin index
205
   * @param k Number of rings around the origin
206
   */
207
  public List<Long> gridDisk(long h3, int k) {
208
    int sz = longToIntSize(h3Api.maxGridDiskSize(k));
1✔
209

210
    long[] out = new long[sz];
1✔
211

212
    h3Api.gridDisk(h3, k, out);
1✔
213

214
    return nonZeroLongArrayToList(out);
1✔
215
  }
216

217
  /**
218
   * Neighboring indexes in all directions, ordered by distance from the origin index.
219
   *
220
   * @param h3Address Origin index
221
   * @param k Number of rings around the origin
222
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
223
   *     closest to origin to farthest.
224
   */
225
  public List<List<String>> gridDiskDistances(String h3Address, int k) {
226
    List<List<Long>> rings = gridDiskDistances(stringToH3(h3Address), k);
1✔
227

228
    return rings.stream().map(this::h3ToStringList).collect(Collectors.toList());
1✔
229
  }
230

231
  /**
232
   * Neighboring indexes in all directions, ordered by distance from the origin index.
233
   *
234
   * @param h3 Origin index
235
   * @param k Number of rings around the origin
236
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
237
   *     closest to origin to farthest.
238
   */
239
  public List<List<Long>> gridDiskDistances(long h3, int k) {
240
    int sz = longToIntSize(h3Api.maxGridDiskSize(k));
1✔
241

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

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

247
    List<List<Long>> ret = new ArrayList<>(k + 1);
1✔
248

249
    for (int i = 0; i <= k; i++) {
1✔
250
      ret.add(new ArrayList<>());
1✔
251
    }
252

253
    for (int i = 0; i < sz; i++) {
1✔
254
      long nextH3 = out[i];
1✔
255
      if (nextH3 != INVALID_INDEX) {
1✔
256
        ret.get(distances[i]).add(nextH3);
1✔
257
      }
258
    }
259

260
    return ret;
1✔
261
  }
262

263
  /**
264
   * Returns in order neighbor traversal.
265
   *
266
   * @param h3Address Origin hexagon index
267
   * @param k Number of rings around the origin
268
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
269
   *     closest to origin to farthest.
270
   */
271
  public List<List<String>> gridDiskUnsafe(String h3Address, int k) {
272
    List<List<Long>> rings = gridDiskUnsafe(stringToH3(h3Address), k);
1✔
273

274
    return rings.stream().map(this::h3ToStringList).collect(Collectors.toList());
1✔
275
  }
276

277
  /**
278
   * Returns in order neighbor traversal.
279
   *
280
   * @param h3 Origin hexagon index
281
   * @param k Number of rings around the origin
282
   * @return A list of rings, each of which is a list of addresses. The rings are in order from
283
   *     closest to origin to farthest.
284
   */
285
  public List<List<Long>> gridDiskUnsafe(long h3, int k) {
286
    int sz = longToIntSize(h3Api.maxGridDiskSize(k));
1✔
287

288
    long[] out = new long[sz];
1✔
289

290
    h3Api.gridDiskUnsafe(h3, k, out);
1✔
291

292
    List<List<Long>> ret = new ArrayList<>(k + 1);
1✔
293

294
    List<Long> ring = null;
1✔
295
    int currentK = 0;
1✔
296
    int nextRing = 0;
1✔
297

298
    for (int i = 0; i < sz; i++) {
1✔
299
      // Check if we've reached the index of the next ring.
300
      if (i == nextRing) {
1✔
301
        ring = new ArrayList<>();
1✔
302
        ret.add(ring);
1✔
303

304
        // Determine the start index of the next ring.
305
        // k=0 is a special case of size 1.
306
        if (currentK == 0) {
1✔
307
          nextRing = 1;
1✔
308
        } else {
309
          nextRing += (6 * currentK);
1✔
310
        }
311
        currentK++;
1✔
312
      }
313

314
      long h = out[i];
1✔
315
      ring.add(h);
1✔
316
    }
317

318
    return ret;
1✔
319
  }
320

321
  /**
322
   * Returns in order neighbor traversal, of indexes with distance of <code>k</code>.
323
   *
324
   * @param h3Address Origin index
325
   * @param k Number of rings around the origin
326
   * @return All indexes <code>k</code> away from the origin
327
   */
328
  public List<String> gridRingUnsafe(String h3Address, int k) {
329
    return h3ToStringList(gridRingUnsafe(stringToH3(h3Address), k));
1✔
330
  }
331

332
  /**
333
   * Returns in order neighbor traversal, of indexes with distance of <code>k</code>.
334
   *
335
   * @param h3 Origin index
336
   * @param k Number of rings around the origin
337
   * @return All indexes <code>k</code> away from the origin
338
   */
339
  public List<Long> gridRingUnsafe(long h3, int k) {
340
    int sz = k == 0 ? 1 : 6 * k;
1✔
341

342
    long[] out = new long[sz];
1✔
343

344
    h3Api.gridRingUnsafe(h3, k, out);
1✔
345

346
    return nonZeroLongArrayToList(out);
1✔
347
  }
348

349
  /**
350
   * Returns the distance between <code>a</code> and <code>b</code>. This is the grid distance, or
351
   * distance expressed in number of H3 cells.
352
   *
353
   * <p>In some cases H3 cannot compute the distance between two indexes. This can happen because:
354
   *
355
   * <ul>
356
   *   <li>The indexes are not comparable (difference resolutions, etc)
357
   *   <li>The distance is greater than the H3 core library supports
358
   *   <li>The H3 library does not support finding the distance between the two cells, because of
359
   *       pentagonal distortion.
360
   * </ul>
361
   *
362
   * @param a An H3 index
363
   * @param b Another H3 index
364
   * @return Distance between the two in grid cells
365
   */
366
  public long gridDistance(String a, String b) {
367
    return gridDistance(stringToH3(a), stringToH3(b));
1✔
368
  }
369

370
  /**
371
   * Returns the distance between <code>a</code> and <code>b</code>. This is the grid distance, or
372
   * distance expressed in number of H3 cells.
373
   *
374
   * <p>In some cases H3 cannot compute the distance between two indexes. This can happen because:
375
   *
376
   * <ul>
377
   *   <li>The indexes are not comparable (difference resolutions, etc)
378
   *   <li>The distance is greater than the H3 core library supports
379
   *   <li>The H3 library does not support finding the distance between the two cells, because of
380
   *       pentagonal distortion.
381
   * </ul>
382
   *
383
   * @param a An H3 index
384
   * @param b Another H3 index
385
   * @return Distance between the two in grid cells
386
   */
387
  public long gridDistance(long a, long b) {
388
    return h3Api.gridDistance(a, b);
1✔
389
  }
390

391
  /**
392
   * Converts <code>h3</code> to IJ coordinates in a local coordinate space defined by <code>origin
393
   * </code>.
394
   *
395
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
396
   * distortion. IJ coordinates are only comparable if they came from the same origin.
397
   *
398
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
399
   * different versions of H3.
400
   *
401
   * @param origin Anchoring index for the local coordinate space.
402
   * @param h3 Index to find the coordinates of.
403
   * @return Coordinates for <code>h3</code> in the local coordinate space.
404
   */
405
  public CoordIJ cellToLocalIj(long origin, long h3) {
406
    final int[] coords = new int[2];
1✔
407
    h3Api.cellToLocalIj(origin, h3, coords);
1✔
408
    return new CoordIJ(coords[0], coords[1]);
1✔
409
  }
410

411
  /**
412
   * Converts <code>h3Address</code> to IJ coordinates in a local coordinate space defined by <code>
413
   * originAddress</code>.
414
   *
415
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
416
   * distortion. IJ coordinates are only comparable if they came from the same origin.
417
   *
418
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
419
   * different versions of H3.
420
   *
421
   * @param originAddress Anchoring index for the local coordinate space.
422
   * @param h3Address Index to find the coordinates of.
423
   * @return Coordinates for <code>h3</code> in the local coordinate space.
424
   */
425
  public CoordIJ cellToLocalIj(String originAddress, String h3Address) {
426
    return cellToLocalIj(stringToH3(originAddress), stringToH3(h3Address));
1✔
427
  }
428

429
  /**
430
   * Converts the IJ coordinates to an index, using a local IJ coordinate space anchored by <code>
431
   * origin</code>.
432
   *
433
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
434
   * distortion. IJ coordinates are only comparable if they came from the same origin.
435
   *
436
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
437
   * different versions of H3.
438
   *
439
   * @param origin Anchoring index for the local coordinate space.
440
   * @param ij Coordinates in the local IJ coordinate space.
441
   * @return Index represented by <code>ij</code>
442
   */
443
  public long localIjToCell(long origin, CoordIJ ij) {
444
    return h3Api.localIjToCell(origin, ij.i, ij.j);
1✔
445
  }
446

447
  /**
448
   * Converts the IJ coordinates to an index, using a local IJ coordinate space anchored by <code>
449
   * origin</code>.
450
   *
451
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
452
   * distortion. IJ coordinates are only comparable if they came from the same origin.
453
   *
454
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
455
   * different versions of H3.
456
   *
457
   * @param originAddress Anchoring index for the local coordinate space.
458
   * @param ij Coordinates in the local IJ coordinate space.
459
   * @return Index represented by <code>ij</code>
460
   */
461
  public String localIjToCell(String originAddress, CoordIJ ij) {
462
    return h3ToString(localIjToCell(stringToH3(originAddress), ij));
1✔
463
  }
464

465
  /**
466
   * Given two H3 indexes, return the line of indexes between them (inclusive of endpoints).
467
   *
468
   * <p>This function may fail to find the line between two indexes, for example if they are very
469
   * far apart. It may also fail when finding distances for indexes on opposite sides of a pentagon.
470
   *
471
   * <p>Notes:
472
   *
473
   * <ul>
474
   *   <li>The specific output of this function should not be considered stable across library
475
   *       versions. The only guarantees the library provides are that the line length will be
476
   *       `h3Distance(start, end) + 1` and that every index in the line will be a neighbor of the
477
   *       preceding index.
478
   *   <li>Lines are drawn in grid space, and may not correspond exactly to either Cartesian lines
479
   *       or great arcs.
480
   * </ul>
481
   *
482
   * @param startAddress Start index of the line
483
   * @param endAddress End index of the line
484
   * @return Indexes making up the line.
485
   */
486
  public List<String> gridPathCells(String startAddress, String endAddress) {
487
    return h3ToStringList(gridPathCells(stringToH3(startAddress), stringToH3(endAddress)));
1✔
488
  }
489

490
  /**
491
   * Given two H3 indexes, return the line of indexes between them (inclusive of endpoints).
492
   *
493
   * <p>This function may fail to find the line between two indexes, for example if they are very
494
   * far apart. It may also fail when finding distances for indexes on opposite sides of a pentagon.
495
   *
496
   * <p>Notes:
497
   *
498
   * <ul>
499
   *   <li>The specific output of this function should not be considered stable across library
500
   *       versions. The only guarantees the library provides are that the line length will be
501
   *       `h3Distance(start, end) + 1` and that every index in the line will be a neighbor of the
502
   *       preceding index.
503
   *   <li>Lines are drawn in grid space, and may not correspond exactly to either Cartesian lines
504
   *       or great arcs.
505
   * </ul>
506
   *
507
   * @param start Start index of the line
508
   * @param end End index of the line
509
   * @return Indexes making up the line.
510
   */
511
  public List<Long> gridPathCells(long start, long end) {
512
    int size = longToIntSize(h3Api.gridPathCellsSize(start, end));
1✔
513

514
    long[] results = new long[size];
1✔
515
    h3Api.gridPathCells(start, end, results);
1✔
516

517
    return nonZeroLongArrayToList(results);
1✔
518
  }
519

520
  /**
521
   * Finds indexes within the given geopolygon.
522
   *
523
   * @param points Outline geopolygon
524
   * @param holes Geopolygons of any internal holes
525
   * @param res Resolution of the desired indexes
526
   */
527
  public List<String> polygonToCellAddressesExperimental(
528
      List<LatLng> points, List<List<LatLng>> holes, int res, PolygonToCellsFlags flags) {
529
    return h3ToStringList(polygonToCellsExperimental(points, holes, res, flags));
×
530
  }
531

532
  /**
533
   * Finds indexes within the given geopolygon.
534
   *
535
   * @param points Outline geopolygon
536
   * @param holes Geopolygon of any internal holes
537
   * @param res Resolution of the desired indexes
538
   * @throws IllegalArgumentException Invalid resolution
539
   */
540
  public List<Long> polygonToCellsExperimental(
541
      List<LatLng> points, List<List<LatLng>> holes, int res, PolygonToCellsFlags flags) {
542
    checkResolution(res);
1✔
543

544
    // pack the data for use by the polyfill JNI call
545
    double[] verts = new double[points.size() * 2];
1✔
546
    packGeofenceVertices(verts, points, 0);
1✔
547
    int[] holeSizes = new int[0];
1✔
548
    double[] holeVerts = new double[0];
1✔
549
    if (holes != null) {
1!
550
      int holesSize = holes.size();
×
551
      holeSizes = new int[holesSize];
×
552
      int totalSize = 0;
×
553
      for (int i = 0; i < holesSize; i++) {
×
554
        int holeSize = holes.get(i).size() * 2;
×
555
        totalSize += holeSize;
×
556
        // Note we are storing the number of doubles
557
        holeSizes[i] = holeSize;
×
558
      }
559
      holeVerts = new double[totalSize];
×
560
      int offset = 0;
×
561
      for (int i = 0; i < holesSize; i++) {
×
562
        offset = packGeofenceVertices(holeVerts, holes.get(i), offset);
×
563
      }
564
    }
565

566
    int flagsInt = flags.toInt();
1✔
567
    int sz =
1✔
568
        longToIntSize(
1✔
569
            h3Api.maxPolygonToCellsSizeExperimental(verts, holeSizes, holeVerts, res, flagsInt));
1✔
570

571
    long[] results = new long[sz];
1✔
572

573
    h3Api.polygonToCellsExperimental(verts, holeSizes, holeVerts, res, flagsInt, results);
1✔
574

575
    return nonZeroLongArrayToList(results);
1✔
576
  }
577

578
  /**
579
   * Finds indexes within the given geopolygon.
580
   *
581
   * @param points Outline geopolygon
582
   * @param holes Geopolygons of any internal holes
583
   * @param res Resolution of the desired indexes
584
   */
585
  public List<String> polygonToCellAddresses(
586
      List<LatLng> points, List<List<LatLng>> holes, int res) {
587
    return h3ToStringList(polygonToCells(points, holes, res));
1✔
588
  }
589

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

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

623
    int flags = 0;
1✔
624
    int sz = longToIntSize(h3Api.maxPolygonToCellsSize(verts, holeSizes, holeVerts, res, flags));
1✔
625

626
    long[] results = new long[sz];
1✔
627

628
    h3Api.polygonToCells(verts, holeSizes, holeVerts, res, flags, results);
1✔
629

630
    return nonZeroLongArrayToList(results);
1✔
631
  }
632

633
  /**
634
   * Interleave the pairs in the given double array.
635
   *
636
   * @return Next offset to begin filling from
637
   */
638
  private static int packGeofenceVertices(double[] arr, List<LatLng> original, int offset) {
639
    int newOffset = (original.size() * 2) + offset;
1✔
640
    assert arr.length >= newOffset;
1!
641

642
    for (int i = 0, size = original.size(); i < size; i++) {
1✔
643
      LatLng coord = original.get(i);
1✔
644

645
      int firstOffset = (i * 2) + offset;
1✔
646
      int secondOffset = firstOffset + 1;
1✔
647
      arr[firstOffset] = toRadians(coord.lat);
1✔
648
      arr[secondOffset] = toRadians(coord.lng);
1✔
649
    }
650

651
    return newOffset;
1✔
652
  }
653

654
  /** Create polygons from a set of contiguous indexes */
655
  public List<List<List<LatLng>>> cellAddressesToMultiPolygon(
656
      Collection<String> h3Addresses, boolean geoJson) {
657
    List<Long> indices = stringToH3List(h3Addresses);
1✔
658

659
    return cellsToMultiPolygon(indices, geoJson);
1✔
660
  }
661

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

666
    ArrayList<List<List<LatLng>>> result = new ArrayList<>();
1✔
667

668
    h3Api.cellsToLinkedMultiPolygon(h3AsArray, result);
1✔
669

670
    // For each polygon
671
    for (List<List<LatLng>> loops : result) {
1✔
672
      // For each loop within the polygon (first being the outline,
673
      // further loops being "holes" or exclusions in the polygon.)
674
      for (List<LatLng> loop : loops) {
1✔
675
        // For each coordinate in the loop, we need to convert to degrees,
676
        // and ensure the correct ordering (whether geoJson or not.)
677
        for (int vectorInLoop = 0, size = loop.size(); vectorInLoop < size; vectorInLoop++) {
1✔
678
          final LatLng v = loop.get(vectorInLoop);
1✔
679
          final double origLat = toDegrees(v.lat);
1✔
680
          final double origLng = toDegrees(v.lng);
1✔
681

682
          final LatLng replacement = new LatLng(origLat, origLng);
1✔
683

684
          loop.set(vectorInLoop, replacement);
1✔
685
        }
686

687
        if (geoJson && loop.size() > 0) {
1!
688
          // geoJson requires closing the loop
689
          loop.add(loop.get(0));
1✔
690
        }
691
      }
1✔
692
    }
1✔
693

694
    return result;
1✔
695
  }
696

697
  /** Returns the resolution of the provided index */
698
  public int getResolution(String h3Address) {
699
    return getResolution(stringToH3(h3Address));
1✔
700
  }
701

702
  /** Returns the resolution of the provided index */
703
  public int getResolution(long h3) {
704
    return (int) ((h3 & H3_RES_MASK) >> H3_RES_OFFSET);
1✔
705
  }
706

707
  /**
708
   * Returns the parent of the index at the given resolution.
709
   *
710
   * @param h3 H3 index.
711
   * @param res Resolution of the parent, <code>0 &lt;= res &lt;= h3GetResolution(h3)</code>
712
   * @throws IllegalArgumentException <code>res</code> is not between 0 and the resolution of <code>
713
   *     h3</code>, inclusive.
714
   */
715
  public long cellToParent(long h3, int res) {
716
    // This is a ported version of h3ToParent from h3core.
717

718
    int childRes = (int) ((h3 & H3_RES_MASK) >> H3_RES_OFFSET);
1✔
719
    if (res < 0 || res > childRes) {
1✔
720
      throw new IllegalArgumentException(
1✔
721
          String.format("res (%d) must be between 0 and %d, inclusive", res, childRes));
1✔
722
    } else if (res == childRes) {
1✔
723
      return h3;
1✔
724
    }
725

726
    // newRes is the bits that need to be set to set the given resolution.
727
    long newRes = (long) res << H3_RES_OFFSET;
1✔
728
    long digitMaskForRes = H3_DIGIT_MASK;
1✔
729
    for (int i = 0; i < res; i++) {
1✔
730
      digitMaskForRes >>= 3L;
1✔
731
    }
732

733
    return (h3 & H3_RES_MASK_NEGATIVE) | newRes | digitMaskForRes;
1✔
734
  }
735

736
  /**
737
   * Returns the parent of the index at the given resolution.
738
   *
739
   * @param h3Address H3 index.
740
   * @param res Resolution of the parent, <code>0 &lt;= res &lt;= h3GetResolution(h3)</code>
741
   */
742
  public String cellToParentAddress(String h3Address, int res) {
743
    long parent = cellToParent(stringToH3(h3Address), res);
1✔
744
    return h3ToString(parent);
1✔
745
  }
746

747
  /**
748
   * Provides the children of the index at the given resolution.
749
   *
750
   * @param childRes Resolution of the children
751
   */
752
  public List<String> cellToChildren(String h3Address, int childRes) {
753
    return h3ToStringList(cellToChildren(stringToH3(h3Address), childRes));
1✔
754
  }
755

756
  /**
757
   * Provides the children of the index at the given resolution.
758
   *
759
   * @param h3 H3 index.
760
   * @param childRes Resolution of the children
761
   * @throws IllegalArgumentException Invalid resolution
762
   */
763
  public List<Long> cellToChildren(long h3, int childRes) {
764
    checkResolution(childRes);
1✔
765

766
    int sz = longToIntSize(h3Api.cellToChildrenSize(h3, childRes));
1✔
767

768
    long[] out = new long[sz];
1✔
769

770
    h3Api.cellToChildren(h3, childRes, out);
1✔
771

772
    return nonZeroLongArrayToList(out);
1✔
773
  }
774

775
  /**
776
   * Returns the center child at the given resolution.
777
   *
778
   * @param h3 Parent H3 index
779
   * @param childRes Resolution of the child
780
   * @throws IllegalArgumentException Invalid resolution (e.g. coarser than the parent)
781
   */
782
  public String cellToCenterChild(String h3, int childRes) {
783
    return h3ToString(cellToCenterChild(stringToH3(h3), childRes));
1✔
784
  }
785

786
  /** Returns the number of children the cell index has at the given resolution. */
787
  public long cellToChildrenSize(long cell, int childRes) {
788
    return h3Api.cellToChildrenSize(cell, childRes);
1✔
789
  }
790

791
  /** Returns the number of children the cell index has at the given resolution. */
792
  public long cellToChildrenSize(String cellAddress, int childRes) {
793
    return cellToChildrenSize(stringToH3(cellAddress), childRes);
1✔
794
  }
795

796
  /**
797
   * Returns the center child at the given resolution.
798
   *
799
   * @param h3 Parent H3 index
800
   * @param childRes Resolution of the child
801
   * @throws IllegalArgumentException Invalid resolution (e.g. coarser than the parent)
802
   */
803
  public long cellToCenterChild(long h3, int childRes) {
804
    checkResolution(childRes);
1✔
805

806
    long result = h3Api.cellToCenterChild(h3, childRes);
1✔
807

808
    return result;
1✔
809
  }
810

811
  /**
812
   * Determines if an index is Class III or Class II.
813
   *
814
   * @return <code>true</code> if the index is Class III
815
   */
816
  public boolean isResClassIII(String h3Address) {
817
    return isResClassIII(stringToH3(h3Address));
1✔
818
  }
819

820
  /**
821
   * Determines if an index is Class III or Class II.
822
   *
823
   * @param h3 H3 index.
824
   * @return <code>true</code> if the index is Class III
825
   */
826
  public boolean isResClassIII(long h3) {
827
    return getResolution(h3) % 2 != 0;
1✔
828
  }
829

830
  /** Returns a compacted set of indexes, at possibly coarser resolutions. */
831
  public List<String> compactCellAddresses(Collection<String> h3Addresses) {
832
    List<Long> h3 = stringToH3List(h3Addresses);
1✔
833
    List<Long> compacted = compactCells(h3);
1✔
834
    return h3ToStringList(compacted);
1✔
835
  }
836

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

841
    long[] h3AsArray = collectionToLongArray(h3);
1✔
842

843
    long[] out = new long[sz];
1✔
844

845
    h3Api.compactCells(h3AsArray, out);
1✔
846

847
    return nonZeroLongArrayToList(out);
1✔
848
  }
849

850
  /** Uncompacts all the given indexes to resolution <code>res</code>. */
851
  public List<String> uncompactCellAddresses(Collection<String> h3Addresses, int res) {
852
    List<Long> h3 = stringToH3List(h3Addresses);
1✔
853
    List<Long> uncompacted = uncompactCells(h3, res);
1✔
854
    return h3ToStringList(uncompacted);
1✔
855
  }
856

857
  /** Uncompacts all the given indexes to resolution <code>res</code>. */
858
  public List<Long> uncompactCells(Collection<Long> h3, int res) {
859
    checkResolution(res);
1✔
860

861
    long[] h3AsArray = collectionToLongArray(h3);
1✔
862

863
    int sz = longToIntSize(h3Api.uncompactCellsSize(h3AsArray, res));
1✔
864

865
    long[] out = new long[sz];
1✔
866

867
    h3Api.uncompactCells(h3AsArray, res, out);
1✔
868

869
    return nonZeroLongArrayToList(out);
1✔
870
  }
871

872
  /**
873
   * Converts from <code>long</code> representation of an index to <code>String</code>
874
   * representation.
875
   */
876
  public String h3ToString(long h3) {
877
    return Long.toHexString(h3);
1✔
878
  }
879

880
  /**
881
   * Converts from <code>String</code> representation of an index to <code>long</code>
882
   * representation.
883
   */
884
  public long stringToH3(String h3Address) {
885
    return Long.parseUnsignedLong(h3Address, 16);
1✔
886
  }
887

888
  /**
889
   * Calculates the area of the given H3 cell.
890
   *
891
   * @param h3Address Cell to find the area of.
892
   * @param unit Unit to calculate the area in.
893
   * @return Cell area in the given units.
894
   */
895
  public double cellArea(String h3Address, AreaUnit unit) {
896
    return cellArea(stringToH3(h3Address), unit);
1✔
897
  }
898

899
  /**
900
   * Calculates the area of the given H3 cell.
901
   *
902
   * @param h3 Cell to find the area of.
903
   * @param unit Unit to calculate the area in.
904
   * @return Cell area in the given units.
905
   */
906
  public double cellArea(long h3, AreaUnit unit) {
907
    if (unit == AreaUnit.rads2) return h3Api.cellAreaRads2(h3);
1✔
908
    else if (unit == AreaUnit.km2) return h3Api.cellAreaKm2(h3);
1✔
909
    else if (unit == AreaUnit.m2) return h3Api.cellAreaM2(h3);
1✔
910
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
911
  }
912

913
  /**
914
   * Return the distance along the sphere between two points.
915
   *
916
   * @param a First point
917
   * @param b Second point
918
   * @param unit Unit to return the distance in.
919
   * @return Distance from point <code>a</code> to point <code>b</code>
920
   */
921
  public double greatCircleDistance(LatLng a, LatLng b, LengthUnit unit) {
922
    double lat1 = toRadians(a.lat);
1✔
923
    double lng1 = toRadians(a.lng);
1✔
924
    double lat2 = toRadians(b.lat);
1✔
925
    double lng2 = toRadians(b.lng);
1✔
926

927
    if (unit == LengthUnit.rads) return h3Api.greatCircleDistanceRads(lat1, lng1, lat2, lng2);
1✔
928
    else if (unit == LengthUnit.km) return h3Api.greatCircleDistanceKm(lat1, lng1, lat2, lng2);
1✔
929
    else if (unit == LengthUnit.m) return h3Api.greatCircleDistanceM(lat1, lng1, lat2, lng2);
1✔
930
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
931
  }
932

933
  /**
934
   * Calculate the edge length of the given H3 edge.
935
   *
936
   * @param edgeAddress Edge to find the edge length of.
937
   * @param unit Unit of measure to use.
938
   * @return Length of the given edge.
939
   */
940
  public double edgeLength(String edgeAddress, LengthUnit unit) {
941
    return edgeLength(stringToH3(edgeAddress), unit);
1✔
942
  }
943

944
  /**
945
   * Calculate the edge length of the given H3 edge.
946
   *
947
   * @param edge Edge to find the edge length of.
948
   * @param unit Unit of measure to use.
949
   * @return Length of the given edge.
950
   */
951
  public double edgeLength(long edge, LengthUnit unit) {
952
    if (unit == LengthUnit.rads) return h3Api.edgeLengthRads(edge);
1✔
953
    else if (unit == LengthUnit.km) return h3Api.edgeLengthKm(edge);
1✔
954
    else if (unit == LengthUnit.m) return h3Api.edgeLengthM(edge);
1✔
955
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
956
  }
957

958
  /**
959
   * Returns the average area in <code>unit</code> for indexes at resolution <code>res</code>.
960
   *
961
   * @throws IllegalArgumentException Invalid parameter value
962
   */
963
  public double getHexagonAreaAvg(int res, AreaUnit unit) {
964
    checkResolution(res);
1✔
965
    if (unit == AreaUnit.km2) return h3Api.getHexagonAreaAvgKm2(res);
1✔
966
    else if (unit == AreaUnit.m2) return h3Api.getHexagonAreaAvgM2(res);
1✔
967
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
968
  }
969

970
  /**
971
   * Returns the average edge length in <code>unit</code> for indexes at resolution <code>res</code>
972
   * .
973
   *
974
   * @throws IllegalArgumentException Invalid parameter value
975
   */
976
  public double getHexagonEdgeLengthAvg(int res, LengthUnit unit) {
977
    checkResolution(res);
1✔
978
    if (unit == LengthUnit.km) return h3Api.getHexagonEdgeLengthAvgKm(res);
1✔
979
    else if (unit == LengthUnit.m) return h3Api.getHexagonEdgeLengthAvgM(res);
1✔
980
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
981
  }
982

983
  /**
984
   * Returns the number of unique H3 indexes at resolution <code>res</code>.
985
   *
986
   * @throws IllegalArgumentException Invalid resolution
987
   */
988
  public long getNumCells(int res) {
989
    checkResolution(res);
1✔
990
    return h3Api.getNumCells(res);
1✔
991
  }
992

993
  /** Returns a collection of all base cells (H3 indexes are resolution 0). */
994
  public Collection<String> getRes0CellAddresses() {
995
    return h3ToStringList(getRes0Cells());
1✔
996
  }
997

998
  /** Returns a collection of all base cells (H3 indexes are resolution 0). */
999
  public Collection<Long> getRes0Cells() {
1000
    long[] indexes = new long[NUM_BASE_CELLS];
1✔
1001
    h3Api.getRes0Cells(indexes);
1✔
1002
    return nonZeroLongArrayToList(indexes);
1✔
1003
  }
1004

1005
  /**
1006
   * Returns a collection of all topologically pentagonal cells at the given resolution.
1007
   *
1008
   * @throws IllegalArgumentException Invalid resolution.
1009
   */
1010
  public Collection<String> getPentagonAddresses(int res) {
1011
    return h3ToStringList(getPentagons(res));
1✔
1012
  }
1013

1014
  /**
1015
   * Returns a collection of all topologically pentagonal cells at the given resolution.
1016
   *
1017
   * @throws IllegalArgumentException Invalid resolution.
1018
   */
1019
  public Collection<Long> getPentagons(int res) {
1020
    checkResolution(res);
1✔
1021
    long[] indexes = new long[NUM_PENTAGONS];
1✔
1022
    h3Api.getPentagons(res, indexes);
1✔
1023
    return nonZeroLongArrayToList(indexes);
1✔
1024
  }
1025

1026
  /** Returns <code>true</code> if the two indexes are neighbors. */
1027
  public boolean areNeighborCells(long a, long b) {
1028
    return h3Api.areNeighborCells(a, b);
1✔
1029
  }
1030

1031
  /** Returns <code>true</code> if the two indexes are neighbors. */
1032
  public boolean areNeighborCells(String a, String b) {
1033
    return areNeighborCells(stringToH3(a), stringToH3(b));
1✔
1034
  }
1035

1036
  /**
1037
   * Returns a unidirectional edge index representing <code>a</code> towards <code>b</code>.
1038
   *
1039
   * @throws IllegalArgumentException The indexes are not neighbors.
1040
   */
1041
  public long cellsToDirectedEdge(long a, long b) {
1042
    return h3Api.cellsToDirectedEdge(a, b);
1✔
1043
  }
1044

1045
  /**
1046
   * Returns a unidirectional edge index representing <code>a</code> towards <code>b</code>.
1047
   *
1048
   * @throws IllegalArgumentException The indexes are not neighbors.
1049
   */
1050
  public String cellsToDirectedEdge(String a, String b) {
1051
    return h3ToString(cellsToDirectedEdge(stringToH3(a), stringToH3(b)));
1✔
1052
  }
1053

1054
  /** Returns <code>true</code> if the given index is a valid unidirectional edge. */
1055
  public boolean isValidDirectedEdge(long h3) {
1056
    return h3Api.isValidDirectedEdge(h3);
1✔
1057
  }
1058

1059
  /** Returns <code>true</code> if the given index is a valid unidirectional edge. */
1060
  public boolean isValidDirectedEdge(String h3) {
1061
    return isValidDirectedEdge(stringToH3(h3));
1✔
1062
  }
1063

1064
  /** Returns the origin index of the given unidirectional edge. */
1065
  public long getDirectedEdgeOrigin(long h3) {
1066
    return h3Api.getDirectedEdgeOrigin(h3);
1✔
1067
  }
1068

1069
  /** Returns the origin index of the given unidirectional edge. */
1070
  public String getDirectedEdgeOrigin(String h3) {
1071
    return h3ToString(getDirectedEdgeOrigin(stringToH3(h3)));
1✔
1072
  }
1073

1074
  /** Returns the destination index of the given unidirectional edge. */
1075
  public long getDirectedEdgeDestination(long h3) {
1076
    return h3Api.getDirectedEdgeDestination(h3);
1✔
1077
  }
1078

1079
  /** Returns the destination index of the given unidirectional edge. */
1080
  public String getDirectedEdgeDestination(String h3) {
1081
    return h3ToString(getDirectedEdgeDestination(stringToH3(h3)));
1✔
1082
  }
1083

1084
  /**
1085
   * Returns the origin and destination indexes (in that order) of the given unidirectional edge.
1086
   */
1087
  public List<Long> directedEdgeToCells(long h3) {
1088
    long[] results = new long[2];
1✔
1089

1090
    // TODO: could be a pair type
1091
    h3Api.directedEdgeToCells(h3, results);
1✔
1092

1093
    return nonZeroLongArrayToList(results);
1✔
1094
  }
1095

1096
  /**
1097
   * Returns the origin and destination indexes (in that order) of the given unidirectional edge.
1098
   */
1099
  public List<String> directedEdgeToCells(String h3) {
1100
    return h3ToStringList(directedEdgeToCells(stringToH3(h3)));
1✔
1101
  }
1102

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

1107
    h3Api.originToDirectedEdges(h3, results);
1✔
1108

1109
    return nonZeroLongArrayToList(results);
1✔
1110
  }
1111

1112
  /** Returns all unidirectional edges originating from the given index. */
1113
  public List<String> originToDirectedEdges(String h3) {
1114
    return h3ToStringList(originToDirectedEdges(stringToH3(h3)));
1✔
1115
  }
1116

1117
  /** Returns a list of coordinates representing the given edge. */
1118
  public List<LatLng> directedEdgeToBoundary(long h3) {
1119
    double[] verts = new double[MAX_CELL_BNDRY_VERTS * 2];
1✔
1120
    int numVerts = h3Api.directedEdgeToBoundary(h3, verts);
1✔
1121
    List<LatLng> out = new ArrayList<>(numVerts);
1✔
1122
    for (int i = 0; i < numVerts; i++) {
1✔
1123
      LatLng coord = new LatLng(toDegrees(verts[i * 2]), toDegrees(verts[(i * 2) + 1]));
1✔
1124
      out.add(coord);
1✔
1125
    }
1126
    return out;
1✔
1127
  }
1128

1129
  /** Returns a list of coordinates representing the given edge. */
1130
  public List<LatLng> directedEdgeToBoundary(String h3) {
1131
    return directedEdgeToBoundary(stringToH3(h3));
1✔
1132
  }
1133

1134
  /**
1135
   * Find all icosahedron faces intersected by a given H3 index, represented as integers from 0-19.
1136
   *
1137
   * @param h3 Index to find icosahedron faces for.
1138
   * @return A collection of faces intersected by the index.
1139
   */
1140
  public Collection<Integer> getIcosahedronFaces(String h3) {
1141
    return getIcosahedronFaces(stringToH3(h3));
1✔
1142
  }
1143

1144
  /**
1145
   * Find all icosahedron faces intersected by a given H3 index, represented as integers from 0-19.
1146
   *
1147
   * @param h3 Index to find icosahedron faces for.
1148
   * @return A collection of faces intersected by the index.
1149
   */
1150
  public Collection<Integer> getIcosahedronFaces(long h3) {
1151
    int maxFaces = h3Api.maxFaceCount(h3);
1✔
1152
    int[] faces = new int[maxFaces];
1✔
1153

1154
    h3Api.getIcosahedronFaces(h3, faces);
1✔
1155

1156
    return IntStream.of(faces).filter(f -> f != -1).boxed().collect(Collectors.toList());
1✔
1157
  }
1158

1159
  public long cellToVertex(long h3, int vertexNum) {
1160
    return h3Api.cellToVertex(h3, vertexNum);
1✔
1161
  }
1162

1163
  public String cellToVertex(String h3Address, int vertexNum) {
1164
    return h3ToString(h3Api.cellToVertex(stringToH3(h3Address), vertexNum));
1✔
1165
  }
1166

1167
  public List<Long> cellToVertexes(long h3) {
1168
    long[] results = new long[6];
1✔
1169
    h3Api.cellToVertexes(h3, results);
1✔
1170
    return nonZeroLongArrayToList(results);
1✔
1171
  }
1172

1173
  public List<String> cellToVertexes(String h3Address) {
1174
    return h3ToStringList(cellToVertexes(stringToH3(h3Address)));
1✔
1175
  }
1176

1177
  public LatLng vertexToLatLng(long h3) {
1178
    double[] results = new double[2];
1✔
1179
    h3Api.vertexToLatLng(h3, results);
1✔
1180
    return new LatLng(toDegrees(results[0]), toDegrees(results[1]));
1✔
1181
  }
1182

1183
  public LatLng vertexToLatLng(String h3Address) {
1184
    return vertexToLatLng(stringToH3(h3Address));
1✔
1185
  }
1186

1187
  public boolean isValidVertex(long h3) {
1188
    return h3Api.isValidVertex(h3);
1✔
1189
  }
1190

1191
  public boolean isValidVertex(String h3Address) {
1192
    return h3Api.isValidVertex(stringToH3(h3Address));
1✔
1193
  }
1194

1195
  /**
1196
   * Returns the position of the child cell within an ordered list of all children of the cell's
1197
   * parent at the specified resolution parentRes.
1198
   */
1199
  public long cellToChildPos(String childAddress, int parentRes) {
1200
    return cellToChildPos(stringToH3(childAddress), parentRes);
1✔
1201
  }
1202

1203
  /**
1204
   * Returns the position of the child cell within an ordered list of all children of the cell's
1205
   * parent at the specified resolution parentRes.
1206
   */
1207
  public long cellToChildPos(long child, int parentRes) {
1208
    return h3Api.cellToChildPos(child, parentRes);
1✔
1209
  }
1210

1211
  /**
1212
   * Returns the child cell at a given position within an ordered list of all children of parent at
1213
   * the specified resolution childRes.
1214
   */
1215
  public long childPosToCell(long childPos, long parent, int childRes) {
1216
    return h3Api.childPosToCell(childPos, parent, childRes);
1✔
1217
  }
1218

1219
  /**
1220
   * Returns the child cell at a given position within an ordered list of all children of parent at
1221
   * the specified resolution childRes.
1222
   */
1223
  public String childPosToCell(long childPos, String parentAddress, int childRes) {
1224
    return h3ToString(childPosToCell(childPos, stringToH3(parentAddress), childRes));
1✔
1225
  }
1226

1227
  /** Transforms a collection of H3 indexes in string form to a list of H3 indexes in long form. */
1228
  private List<Long> stringToH3List(Collection<String> collection) {
1229
    return collection.stream().map(this::stringToH3).collect(Collectors.toList());
1✔
1230
  }
1231

1232
  /** Transforms a list of H3 indexes in long form to a list of H3 indexes in string form. */
1233
  private List<String> h3ToStringList(Collection<Long> collection) {
1234
    return collection.stream().map(this::h3ToString).collect(Collectors.toList());
1✔
1235
  }
1236

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

1241
    for (int i = 0; i < out.length; i++) {
1✔
1242
      long h = out[i];
1✔
1243
      if (h != 0) {
1✔
1244
        ret.add(h);
1✔
1245
      }
1246
    }
1247

1248
    return ret;
1✔
1249
  }
1250

1251
  /** Returns an array of <code>long</code> with the contents of the collection. */
1252
  private static long[] collectionToLongArray(Collection<Long> collection) {
1253
    return collection.stream().mapToLong(Long::longValue).toArray();
1✔
1254
  }
1255

1256
  /**
1257
   * @throws IllegalArgumentException <code>res</code> is not a valid H3 resolution.
1258
   */
1259
  private static void checkResolution(int res) {
1260
    if (res < 0 || res > 15) {
1✔
1261
      throw new IllegalArgumentException(
1✔
1262
          String.format("resolution %d is out of range (must be 0 <= res <= 15)", res));
1✔
1263
    }
1264
  }
1✔
1265

1266
  /**
1267
   * @throws IllegalArgumentException <code>sz</code> cannot be losslessly cast to an <code>int
1268
   *     </code>
1269
   */
1270
  private static int longToIntSize(long sz) {
1271
    if (sz < 0 || sz > Integer.MAX_VALUE) {
1!
1272
      throw new IllegalArgumentException(String.format("size %d is out of range", sz));
1✔
1273
    }
1274
    return (int) sz;
1✔
1275
  }
1276
}
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