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

isaacbrodsky / h3-java / #50

18 Jun 2025 07:51PM UTC coverage: 94.437% (-4.4%) from 98.817%
#50

push

github

web-flow
Update README.md tip banner (#170)

156 of 173 branches covered (90.17%)

Branch coverage included in aggregate %.

523 of 546 relevant lines covered (95.79%)

0.96 hits per line

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

94.67
/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> gridRing(String h3Address, int k) {
329
    return h3ToStringList(gridRing(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> gridRing(long h3, int k) {
340
    int sz = k == 0 ? 1 : 6 * k;
1✔
341

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

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

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

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

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

370
    long[] out = new long[sz];
1✔
371

372
    h3Api.gridRingUnsafe(h3, k, out);
1✔
373

374
    return nonZeroLongArrayToList(out);
1✔
375
  }
376

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

398
  /**
399
   * Returns the distance between <code>a</code> and <code>b</code>. This is the grid distance, or
400
   * distance expressed in number of H3 cells.
401
   *
402
   * <p>In some cases H3 cannot compute the distance between two indexes. This can happen because:
403
   *
404
   * <ul>
405
   *   <li>The indexes are not comparable (difference resolutions, etc)
406
   *   <li>The distance is greater than the H3 core library supports
407
   *   <li>The H3 library does not support finding the distance between the two cells, because of
408
   *       pentagonal distortion.
409
   * </ul>
410
   *
411
   * @param a An H3 index
412
   * @param b Another H3 index
413
   * @return Distance between the two in grid cells
414
   */
415
  public long gridDistance(long a, long b) {
416
    return h3Api.gridDistance(a, b);
1✔
417
  }
418

419
  /**
420
   * Converts <code>h3</code> to IJ coordinates in a local coordinate space defined by <code>origin
421
   * </code>.
422
   *
423
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
424
   * distortion. IJ coordinates are only comparable if they came from the same origin.
425
   *
426
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
427
   * different versions of H3.
428
   *
429
   * @param origin Anchoring index for the local coordinate space.
430
   * @param h3 Index to find the coordinates of.
431
   * @return Coordinates for <code>h3</code> in the local coordinate space.
432
   */
433
  public CoordIJ cellToLocalIj(long origin, long h3) {
434
    final int[] coords = new int[2];
1✔
435
    h3Api.cellToLocalIj(origin, h3, coords);
1✔
436
    return new CoordIJ(coords[0], coords[1]);
1✔
437
  }
438

439
  /**
440
   * Converts <code>h3Address</code> to IJ coordinates in a local coordinate space defined by <code>
441
   * originAddress</code>.
442
   *
443
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
444
   * distortion. IJ coordinates are only comparable if they came from the same origin.
445
   *
446
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
447
   * different versions of H3.
448
   *
449
   * @param originAddress Anchoring index for the local coordinate space.
450
   * @param h3Address Index to find the coordinates of.
451
   * @return Coordinates for <code>h3</code> in the local coordinate space.
452
   */
453
  public CoordIJ cellToLocalIj(String originAddress, String h3Address) {
454
    return cellToLocalIj(stringToH3(originAddress), stringToH3(h3Address));
1✔
455
  }
456

457
  /**
458
   * Converts the IJ coordinates to an index, using a local IJ coordinate space anchored by <code>
459
   * origin</code>.
460
   *
461
   * <p>The local IJ coordinate space may have deleted regions and warping due to pentagon
462
   * distortion. IJ coordinates are only comparable if they came from the same origin.
463
   *
464
   * <p>This function is experimental, and its output is not guaranteed to be compatible across
465
   * different versions of H3.
466
   *
467
   * @param origin Anchoring index for the local coordinate space.
468
   * @param ij Coordinates in the local IJ coordinate space.
469
   * @return Index represented by <code>ij</code>
470
   */
471
  public long localIjToCell(long origin, CoordIJ ij) {
472
    return h3Api.localIjToCell(origin, ij.i, ij.j);
1✔
473
  }
474

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

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

518
  /**
519
   * Given two H3 indexes, return the line of indexes between them (inclusive of endpoints).
520
   *
521
   * <p>This function may fail to find the line between two indexes, for example if they are very
522
   * far apart. It may also fail when finding distances for indexes on opposite sides of a pentagon.
523
   *
524
   * <p>Notes:
525
   *
526
   * <ul>
527
   *   <li>The specific output of this function should not be considered stable across library
528
   *       versions. The only guarantees the library provides are that the line length will be
529
   *       `h3Distance(start, end) + 1` and that every index in the line will be a neighbor of the
530
   *       preceding index.
531
   *   <li>Lines are drawn in grid space, and may not correspond exactly to either Cartesian lines
532
   *       or great arcs.
533
   * </ul>
534
   *
535
   * @param start Start index of the line
536
   * @param end End index of the line
537
   * @return Indexes making up the line.
538
   */
539
  public List<Long> gridPathCells(long start, long end) {
540
    int size = longToIntSize(h3Api.gridPathCellsSize(start, end));
1✔
541

542
    long[] results = new long[size];
1✔
543
    h3Api.gridPathCells(start, end, results);
1✔
544

545
    return nonZeroLongArrayToList(results);
1✔
546
  }
547

548
  /**
549
   * Finds indexes within the given geopolygon.
550
   *
551
   * @param points Outline geopolygon
552
   * @param holes Geopolygons of any internal holes
553
   * @param res Resolution of the desired indexes
554
   */
555
  public List<String> polygonToCellAddressesExperimental(
556
      List<LatLng> points, List<List<LatLng>> holes, int res, PolygonToCellsFlags flags) {
557
    return h3ToStringList(polygonToCellsExperimental(points, holes, res, flags));
×
558
  }
559

560
  /**
561
   * Finds indexes within the given geopolygon.
562
   *
563
   * @param points Outline geopolygon
564
   * @param holes Geopolygon of any internal holes
565
   * @param res Resolution of the desired indexes
566
   * @throws IllegalArgumentException Invalid resolution
567
   */
568
  public List<Long> polygonToCellsExperimental(
569
      List<LatLng> points, List<List<LatLng>> holes, int res, PolygonToCellsFlags flags) {
570
    checkResolution(res);
1✔
571

572
    // pack the data for use by the polyfill JNI call
573
    double[] verts = new double[points.size() * 2];
1✔
574
    packGeofenceVertices(verts, points, 0);
1✔
575
    int[] holeSizes = new int[0];
1✔
576
    double[] holeVerts = new double[0];
1✔
577
    if (holes != null) {
1!
578
      int holesSize = holes.size();
×
579
      holeSizes = new int[holesSize];
×
580
      int totalSize = 0;
×
581
      for (int i = 0; i < holesSize; i++) {
×
582
        int holeSize = holes.get(i).size() * 2;
×
583
        totalSize += holeSize;
×
584
        // Note we are storing the number of doubles
585
        holeSizes[i] = holeSize;
×
586
      }
587
      holeVerts = new double[totalSize];
×
588
      int offset = 0;
×
589
      for (int i = 0; i < holesSize; i++) {
×
590
        offset = packGeofenceVertices(holeVerts, holes.get(i), offset);
×
591
      }
592
    }
593

594
    int flagsInt = flags.toInt();
1✔
595
    int sz =
1✔
596
        longToIntSize(
1✔
597
            h3Api.maxPolygonToCellsSizeExperimental(verts, holeSizes, holeVerts, res, flagsInt));
1✔
598

599
    long[] results = new long[sz];
1✔
600

601
    h3Api.polygonToCellsExperimental(verts, holeSizes, holeVerts, res, flagsInt, results);
1✔
602

603
    return nonZeroLongArrayToList(results);
1✔
604
  }
605

606
  /**
607
   * Finds indexes within the given geopolygon.
608
   *
609
   * @param points Outline geopolygon
610
   * @param holes Geopolygons of any internal holes
611
   * @param res Resolution of the desired indexes
612
   */
613
  public List<String> polygonToCellAddresses(
614
      List<LatLng> points, List<List<LatLng>> holes, int res) {
615
    return h3ToStringList(polygonToCells(points, holes, res));
1✔
616
  }
617

618
  /**
619
   * Finds indexes within the given geopolygon.
620
   *
621
   * @param points Outline geopolygon
622
   * @param holes Geopolygon of any internal holes
623
   * @param res Resolution of the desired indexes
624
   * @throws IllegalArgumentException Invalid resolution
625
   */
626
  public List<Long> polygonToCells(List<LatLng> points, List<List<LatLng>> holes, int res) {
627
    checkResolution(res);
1✔
628

629
    // pack the data for use by the polyfill JNI call
630
    double[] verts = new double[points.size() * 2];
1✔
631
    packGeofenceVertices(verts, points, 0);
1✔
632
    int[] holeSizes = new int[0];
1✔
633
    double[] holeVerts = new double[0];
1✔
634
    if (holes != null) {
1✔
635
      int holesSize = holes.size();
1✔
636
      holeSizes = new int[holesSize];
1✔
637
      int totalSize = 0;
1✔
638
      for (int i = 0; i < holesSize; i++) {
1✔
639
        int holeSize = holes.get(i).size() * 2;
1✔
640
        totalSize += holeSize;
1✔
641
        // Note we are storing the number of doubles
642
        holeSizes[i] = holeSize;
1✔
643
      }
644
      holeVerts = new double[totalSize];
1✔
645
      int offset = 0;
1✔
646
      for (int i = 0; i < holesSize; i++) {
1✔
647
        offset = packGeofenceVertices(holeVerts, holes.get(i), offset);
1✔
648
      }
649
    }
650

651
    int flags = 0;
1✔
652
    int sz = longToIntSize(h3Api.maxPolygonToCellsSize(verts, holeSizes, holeVerts, res, flags));
1✔
653

654
    long[] results = new long[sz];
1✔
655

656
    h3Api.polygonToCells(verts, holeSizes, holeVerts, res, flags, results);
1✔
657

658
    return nonZeroLongArrayToList(results);
1✔
659
  }
660

661
  /**
662
   * Interleave the pairs in the given double array.
663
   *
664
   * @return Next offset to begin filling from
665
   */
666
  private static int packGeofenceVertices(double[] arr, List<LatLng> original, int offset) {
667
    int newOffset = (original.size() * 2) + offset;
1✔
668
    assert arr.length >= newOffset;
1!
669

670
    for (int i = 0, size = original.size(); i < size; i++) {
1✔
671
      LatLng coord = original.get(i);
1✔
672

673
      int firstOffset = (i * 2) + offset;
1✔
674
      int secondOffset = firstOffset + 1;
1✔
675
      arr[firstOffset] = toRadians(coord.lat);
1✔
676
      arr[secondOffset] = toRadians(coord.lng);
1✔
677
    }
678

679
    return newOffset;
1✔
680
  }
681

682
  /** Create polygons from a set of contiguous indexes */
683
  public List<List<List<LatLng>>> cellAddressesToMultiPolygon(
684
      Collection<String> h3Addresses, boolean geoJson) {
685
    List<Long> indices = stringToH3List(h3Addresses);
1✔
686

687
    return cellsToMultiPolygon(indices, geoJson);
1✔
688
  }
689

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

694
    ArrayList<List<List<LatLng>>> result = new ArrayList<>();
1✔
695

696
    h3Api.cellsToLinkedMultiPolygon(h3AsArray, result);
1✔
697

698
    // For each polygon
699
    for (List<List<LatLng>> loops : result) {
1✔
700
      // For each loop within the polygon (first being the outline,
701
      // further loops being "holes" or exclusions in the polygon.)
702
      for (List<LatLng> loop : loops) {
1✔
703
        // For each coordinate in the loop, we need to convert to degrees,
704
        // and ensure the correct ordering (whether geoJson or not.)
705
        for (int vectorInLoop = 0, size = loop.size(); vectorInLoop < size; vectorInLoop++) {
1✔
706
          final LatLng v = loop.get(vectorInLoop);
1✔
707
          final double origLat = toDegrees(v.lat);
1✔
708
          final double origLng = toDegrees(v.lng);
1✔
709

710
          final LatLng replacement = new LatLng(origLat, origLng);
1✔
711

712
          loop.set(vectorInLoop, replacement);
1✔
713
        }
714

715
        if (geoJson && loop.size() > 0) {
1!
716
          // geoJson requires closing the loop
717
          loop.add(loop.get(0));
1✔
718
        }
719
      }
1✔
720
    }
1✔
721

722
    return result;
1✔
723
  }
724

725
  /** Returns the resolution of the provided index */
726
  public int getResolution(String h3Address) {
727
    return getResolution(stringToH3(h3Address));
1✔
728
  }
729

730
  /** Returns the resolution of the provided index */
731
  public int getResolution(long h3) {
732
    return (int) ((h3 & H3_RES_MASK) >> H3_RES_OFFSET);
1✔
733
  }
734

735
  /**
736
   * Returns the parent of the index at the given resolution.
737
   *
738
   * @param h3 H3 index.
739
   * @param res Resolution of the parent, <code>0 &lt;= res &lt;= h3GetResolution(h3)</code>
740
   * @throws IllegalArgumentException <code>res</code> is not between 0 and the resolution of <code>
741
   *     h3</code>, inclusive.
742
   */
743
  public long cellToParent(long h3, int res) {
744
    // This is a ported version of h3ToParent from h3core.
745

746
    int childRes = (int) ((h3 & H3_RES_MASK) >> H3_RES_OFFSET);
1✔
747
    if (res < 0 || res > childRes) {
1✔
748
      throw new IllegalArgumentException(
1✔
749
          String.format("res (%d) must be between 0 and %d, inclusive", res, childRes));
1✔
750
    } else if (res == childRes) {
1✔
751
      return h3;
1✔
752
    }
753

754
    // newRes is the bits that need to be set to set the given resolution.
755
    long newRes = (long) res << H3_RES_OFFSET;
1✔
756
    long digitMaskForRes = H3_DIGIT_MASK;
1✔
757
    for (int i = 0; i < res; i++) {
1✔
758
      digitMaskForRes >>= 3L;
1✔
759
    }
760

761
    return (h3 & H3_RES_MASK_NEGATIVE) | newRes | digitMaskForRes;
1✔
762
  }
763

764
  /**
765
   * Returns the parent of the index at the given resolution.
766
   *
767
   * @param h3Address H3 index.
768
   * @param res Resolution of the parent, <code>0 &lt;= res &lt;= h3GetResolution(h3)</code>
769
   */
770
  public String cellToParentAddress(String h3Address, int res) {
771
    long parent = cellToParent(stringToH3(h3Address), res);
1✔
772
    return h3ToString(parent);
1✔
773
  }
774

775
  /**
776
   * Provides the children of the index at the given resolution.
777
   *
778
   * @param childRes Resolution of the children
779
   */
780
  public List<String> cellToChildren(String h3Address, int childRes) {
781
    return h3ToStringList(cellToChildren(stringToH3(h3Address), childRes));
1✔
782
  }
783

784
  /**
785
   * Provides the children of the index at the given resolution.
786
   *
787
   * @param h3 H3 index.
788
   * @param childRes Resolution of the children
789
   * @throws IllegalArgumentException Invalid resolution
790
   */
791
  public List<Long> cellToChildren(long h3, int childRes) {
792
    checkResolution(childRes);
1✔
793

794
    int sz = longToIntSize(h3Api.cellToChildrenSize(h3, childRes));
1✔
795

796
    long[] out = new long[sz];
1✔
797

798
    h3Api.cellToChildren(h3, childRes, out);
1✔
799

800
    return nonZeroLongArrayToList(out);
1✔
801
  }
802

803
  /**
804
   * Returns the center child at the given resolution.
805
   *
806
   * @param h3 Parent H3 index
807
   * @param childRes Resolution of the child
808
   * @throws IllegalArgumentException Invalid resolution (e.g. coarser than the parent)
809
   */
810
  public String cellToCenterChild(String h3, int childRes) {
811
    return h3ToString(cellToCenterChild(stringToH3(h3), childRes));
1✔
812
  }
813

814
  /** Returns the number of children the cell index has at the given resolution. */
815
  public long cellToChildrenSize(long cell, int childRes) {
816
    return h3Api.cellToChildrenSize(cell, childRes);
1✔
817
  }
818

819
  /** Returns the number of children the cell index has at the given resolution. */
820
  public long cellToChildrenSize(String cellAddress, int childRes) {
821
    return cellToChildrenSize(stringToH3(cellAddress), childRes);
1✔
822
  }
823

824
  /**
825
   * Returns the center child at the given resolution.
826
   *
827
   * @param h3 Parent H3 index
828
   * @param childRes Resolution of the child
829
   * @throws IllegalArgumentException Invalid resolution (e.g. coarser than the parent)
830
   */
831
  public long cellToCenterChild(long h3, int childRes) {
832
    checkResolution(childRes);
1✔
833

834
    long result = h3Api.cellToCenterChild(h3, childRes);
1✔
835

836
    return result;
1✔
837
  }
838

839
  /**
840
   * Determines if an index is Class III or Class II.
841
   *
842
   * @return <code>true</code> if the index is Class III
843
   */
844
  public boolean isResClassIII(String h3Address) {
845
    return isResClassIII(stringToH3(h3Address));
1✔
846
  }
847

848
  /**
849
   * Determines if an index is Class III or Class II.
850
   *
851
   * @param h3 H3 index.
852
   * @return <code>true</code> if the index is Class III
853
   */
854
  public boolean isResClassIII(long h3) {
855
    return getResolution(h3) % 2 != 0;
1✔
856
  }
857

858
  /** Returns a compacted set of indexes, at possibly coarser resolutions. */
859
  public List<String> compactCellAddresses(Collection<String> h3Addresses) {
860
    List<Long> h3 = stringToH3List(h3Addresses);
1✔
861
    List<Long> compacted = compactCells(h3);
1✔
862
    return h3ToStringList(compacted);
1✔
863
  }
864

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

869
    long[] h3AsArray = collectionToLongArray(h3);
1✔
870

871
    long[] out = new long[sz];
1✔
872

873
    h3Api.compactCells(h3AsArray, out);
1✔
874

875
    return nonZeroLongArrayToList(out);
1✔
876
  }
877

878
  /** Uncompacts all the given indexes to resolution <code>res</code>. */
879
  public List<String> uncompactCellAddresses(Collection<String> h3Addresses, int res) {
880
    List<Long> h3 = stringToH3List(h3Addresses);
1✔
881
    List<Long> uncompacted = uncompactCells(h3, res);
1✔
882
    return h3ToStringList(uncompacted);
1✔
883
  }
884

885
  /** Uncompacts all the given indexes to resolution <code>res</code>. */
886
  public List<Long> uncompactCells(Collection<Long> h3, int res) {
887
    checkResolution(res);
1✔
888

889
    long[] h3AsArray = collectionToLongArray(h3);
1✔
890

891
    int sz = longToIntSize(h3Api.uncompactCellsSize(h3AsArray, res));
1✔
892

893
    long[] out = new long[sz];
1✔
894

895
    h3Api.uncompactCells(h3AsArray, res, out);
1✔
896

897
    return nonZeroLongArrayToList(out);
1✔
898
  }
899

900
  /**
901
   * Converts from <code>long</code> representation of an index to <code>String</code>
902
   * representation.
903
   */
904
  public String h3ToString(long h3) {
905
    return Long.toHexString(h3);
1✔
906
  }
907

908
  /**
909
   * Converts from <code>String</code> representation of an index to <code>long</code>
910
   * representation.
911
   */
912
  public long stringToH3(String h3Address) {
913
    return Long.parseUnsignedLong(h3Address, 16);
1✔
914
  }
915

916
  /**
917
   * Calculates the area of the given H3 cell.
918
   *
919
   * @param h3Address Cell to find the area of.
920
   * @param unit Unit to calculate the area in.
921
   * @return Cell area in the given units.
922
   */
923
  public double cellArea(String h3Address, AreaUnit unit) {
924
    return cellArea(stringToH3(h3Address), unit);
1✔
925
  }
926

927
  /**
928
   * Calculates the area of the given H3 cell.
929
   *
930
   * @param h3 Cell to find the area of.
931
   * @param unit Unit to calculate the area in.
932
   * @return Cell area in the given units.
933
   */
934
  public double cellArea(long h3, AreaUnit unit) {
935
    if (unit == AreaUnit.rads2) return h3Api.cellAreaRads2(h3);
1✔
936
    else if (unit == AreaUnit.km2) return h3Api.cellAreaKm2(h3);
1✔
937
    else if (unit == AreaUnit.m2) return h3Api.cellAreaM2(h3);
1✔
938
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
939
  }
940

941
  /**
942
   * Return the distance along the sphere between two points.
943
   *
944
   * @param a First point
945
   * @param b Second point
946
   * @param unit Unit to return the distance in.
947
   * @return Distance from point <code>a</code> to point <code>b</code>
948
   */
949
  public double greatCircleDistance(LatLng a, LatLng b, LengthUnit unit) {
950
    double lat1 = toRadians(a.lat);
1✔
951
    double lng1 = toRadians(a.lng);
1✔
952
    double lat2 = toRadians(b.lat);
1✔
953
    double lng2 = toRadians(b.lng);
1✔
954

955
    if (unit == LengthUnit.rads) return h3Api.greatCircleDistanceRads(lat1, lng1, lat2, lng2);
1✔
956
    else if (unit == LengthUnit.km) return h3Api.greatCircleDistanceKm(lat1, lng1, lat2, lng2);
1✔
957
    else if (unit == LengthUnit.m) return h3Api.greatCircleDistanceM(lat1, lng1, lat2, lng2);
1✔
958
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
959
  }
960

961
  /**
962
   * Calculate the edge length of the given H3 edge.
963
   *
964
   * @param edgeAddress Edge to find the edge length of.
965
   * @param unit Unit of measure to use.
966
   * @return Length of the given edge.
967
   */
968
  public double edgeLength(String edgeAddress, LengthUnit unit) {
969
    return edgeLength(stringToH3(edgeAddress), unit);
1✔
970
  }
971

972
  /**
973
   * Calculate the edge length of the given H3 edge.
974
   *
975
   * @param edge Edge to find the edge length of.
976
   * @param unit Unit of measure to use.
977
   * @return Length of the given edge.
978
   */
979
  public double edgeLength(long edge, LengthUnit unit) {
980
    if (unit == LengthUnit.rads) return h3Api.edgeLengthRads(edge);
1✔
981
    else if (unit == LengthUnit.km) return h3Api.edgeLengthKm(edge);
1✔
982
    else if (unit == LengthUnit.m) return h3Api.edgeLengthM(edge);
1✔
983
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
984
  }
985

986
  /**
987
   * Returns the average area in <code>unit</code> for indexes at resolution <code>res</code>.
988
   *
989
   * @throws IllegalArgumentException Invalid parameter value
990
   */
991
  public double getHexagonAreaAvg(int res, AreaUnit unit) {
992
    checkResolution(res);
1✔
993
    if (unit == AreaUnit.km2) return h3Api.getHexagonAreaAvgKm2(res);
1✔
994
    else if (unit == AreaUnit.m2) return h3Api.getHexagonAreaAvgM2(res);
1✔
995
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
996
  }
997

998
  /**
999
   * Returns the average edge length in <code>unit</code> for indexes at resolution <code>res</code>
1000
   * .
1001
   *
1002
   * @throws IllegalArgumentException Invalid parameter value
1003
   */
1004
  public double getHexagonEdgeLengthAvg(int res, LengthUnit unit) {
1005
    checkResolution(res);
1✔
1006
    if (unit == LengthUnit.km) return h3Api.getHexagonEdgeLengthAvgKm(res);
1✔
1007
    else if (unit == LengthUnit.m) return h3Api.getHexagonEdgeLengthAvgM(res);
1✔
1008
    else throw new IllegalArgumentException(String.format("Invalid unit: %s", unit));
1✔
1009
  }
1010

1011
  /**
1012
   * Returns the number of unique H3 indexes at resolution <code>res</code>.
1013
   *
1014
   * @throws IllegalArgumentException Invalid resolution
1015
   */
1016
  public long getNumCells(int res) {
1017
    checkResolution(res);
1✔
1018
    return h3Api.getNumCells(res);
1✔
1019
  }
1020

1021
  /** Returns a collection of all base cells (H3 indexes are resolution 0). */
1022
  public Collection<String> getRes0CellAddresses() {
1023
    return h3ToStringList(getRes0Cells());
1✔
1024
  }
1025

1026
  /** Returns a collection of all base cells (H3 indexes are resolution 0). */
1027
  public Collection<Long> getRes0Cells() {
1028
    long[] indexes = new long[NUM_BASE_CELLS];
1✔
1029
    h3Api.getRes0Cells(indexes);
1✔
1030
    return nonZeroLongArrayToList(indexes);
1✔
1031
  }
1032

1033
  /**
1034
   * Returns a collection of all topologically pentagonal cells at the given resolution.
1035
   *
1036
   * @throws IllegalArgumentException Invalid resolution.
1037
   */
1038
  public Collection<String> getPentagonAddresses(int res) {
1039
    return h3ToStringList(getPentagons(res));
1✔
1040
  }
1041

1042
  /**
1043
   * Returns a collection of all topologically pentagonal cells at the given resolution.
1044
   *
1045
   * @throws IllegalArgumentException Invalid resolution.
1046
   */
1047
  public Collection<Long> getPentagons(int res) {
1048
    checkResolution(res);
1✔
1049
    long[] indexes = new long[NUM_PENTAGONS];
1✔
1050
    h3Api.getPentagons(res, indexes);
1✔
1051
    return nonZeroLongArrayToList(indexes);
1✔
1052
  }
1053

1054
  /** Returns <code>true</code> if the two indexes are neighbors. */
1055
  public boolean areNeighborCells(long a, long b) {
1056
    return h3Api.areNeighborCells(a, b);
1✔
1057
  }
1058

1059
  /** Returns <code>true</code> if the two indexes are neighbors. */
1060
  public boolean areNeighborCells(String a, String b) {
1061
    return areNeighborCells(stringToH3(a), stringToH3(b));
1✔
1062
  }
1063

1064
  /**
1065
   * Returns a unidirectional edge index representing <code>a</code> towards <code>b</code>.
1066
   *
1067
   * @throws IllegalArgumentException The indexes are not neighbors.
1068
   */
1069
  public long cellsToDirectedEdge(long a, long b) {
1070
    return h3Api.cellsToDirectedEdge(a, b);
1✔
1071
  }
1072

1073
  /**
1074
   * Returns a unidirectional edge index representing <code>a</code> towards <code>b</code>.
1075
   *
1076
   * @throws IllegalArgumentException The indexes are not neighbors.
1077
   */
1078
  public String cellsToDirectedEdge(String a, String b) {
1079
    return h3ToString(cellsToDirectedEdge(stringToH3(a), stringToH3(b)));
1✔
1080
  }
1081

1082
  /** Returns <code>true</code> if the given index is a valid unidirectional edge. */
1083
  public boolean isValidDirectedEdge(long h3) {
1084
    return h3Api.isValidDirectedEdge(h3);
1✔
1085
  }
1086

1087
  /** Returns <code>true</code> if the given index is a valid unidirectional edge. */
1088
  public boolean isValidDirectedEdge(String h3) {
1089
    return isValidDirectedEdge(stringToH3(h3));
1✔
1090
  }
1091

1092
  /** Returns the origin index of the given unidirectional edge. */
1093
  public long getDirectedEdgeOrigin(long h3) {
1094
    return h3Api.getDirectedEdgeOrigin(h3);
1✔
1095
  }
1096

1097
  /** Returns the origin index of the given unidirectional edge. */
1098
  public String getDirectedEdgeOrigin(String h3) {
1099
    return h3ToString(getDirectedEdgeOrigin(stringToH3(h3)));
1✔
1100
  }
1101

1102
  /** Returns the destination index of the given unidirectional edge. */
1103
  public long getDirectedEdgeDestination(long h3) {
1104
    return h3Api.getDirectedEdgeDestination(h3);
1✔
1105
  }
1106

1107
  /** Returns the destination index of the given unidirectional edge. */
1108
  public String getDirectedEdgeDestination(String h3) {
1109
    return h3ToString(getDirectedEdgeDestination(stringToH3(h3)));
1✔
1110
  }
1111

1112
  /**
1113
   * Returns the origin and destination indexes (in that order) of the given unidirectional edge.
1114
   */
1115
  public List<Long> directedEdgeToCells(long h3) {
1116
    long[] results = new long[2];
1✔
1117

1118
    // TODO: could be a pair type
1119
    h3Api.directedEdgeToCells(h3, results);
1✔
1120

1121
    return nonZeroLongArrayToList(results);
1✔
1122
  }
1123

1124
  /**
1125
   * Returns the origin and destination indexes (in that order) of the given unidirectional edge.
1126
   */
1127
  public List<String> directedEdgeToCells(String h3) {
1128
    return h3ToStringList(directedEdgeToCells(stringToH3(h3)));
1✔
1129
  }
1130

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

1135
    h3Api.originToDirectedEdges(h3, results);
1✔
1136

1137
    return nonZeroLongArrayToList(results);
1✔
1138
  }
1139

1140
  /** Returns all unidirectional edges originating from the given index. */
1141
  public List<String> originToDirectedEdges(String h3) {
1142
    return h3ToStringList(originToDirectedEdges(stringToH3(h3)));
1✔
1143
  }
1144

1145
  /** Returns a list of coordinates representing the given edge. */
1146
  public List<LatLng> directedEdgeToBoundary(long h3) {
1147
    double[] verts = new double[MAX_CELL_BNDRY_VERTS * 2];
1✔
1148
    int numVerts = h3Api.directedEdgeToBoundary(h3, verts);
1✔
1149
    List<LatLng> out = new ArrayList<>(numVerts);
1✔
1150
    for (int i = 0; i < numVerts; i++) {
1✔
1151
      LatLng coord = new LatLng(toDegrees(verts[i * 2]), toDegrees(verts[(i * 2) + 1]));
1✔
1152
      out.add(coord);
1✔
1153
    }
1154
    return out;
1✔
1155
  }
1156

1157
  /** Returns a list of coordinates representing the given edge. */
1158
  public List<LatLng> directedEdgeToBoundary(String h3) {
1159
    return directedEdgeToBoundary(stringToH3(h3));
1✔
1160
  }
1161

1162
  /**
1163
   * Find all icosahedron faces intersected by a given H3 index, represented as integers from 0-19.
1164
   *
1165
   * @param h3 Index to find icosahedron faces for.
1166
   * @return A collection of faces intersected by the index.
1167
   */
1168
  public Collection<Integer> getIcosahedronFaces(String h3) {
1169
    return getIcosahedronFaces(stringToH3(h3));
1✔
1170
  }
1171

1172
  /**
1173
   * Find all icosahedron faces intersected by a given H3 index, represented as integers from 0-19.
1174
   *
1175
   * @param h3 Index to find icosahedron faces for.
1176
   * @return A collection of faces intersected by the index.
1177
   */
1178
  public Collection<Integer> getIcosahedronFaces(long h3) {
1179
    int maxFaces = h3Api.maxFaceCount(h3);
1✔
1180
    int[] faces = new int[maxFaces];
1✔
1181

1182
    h3Api.getIcosahedronFaces(h3, faces);
1✔
1183

1184
    return IntStream.of(faces).filter(f -> f != -1).boxed().collect(Collectors.toList());
1✔
1185
  }
1186

1187
  public long cellToVertex(long h3, int vertexNum) {
1188
    return h3Api.cellToVertex(h3, vertexNum);
1✔
1189
  }
1190

1191
  public String cellToVertex(String h3Address, int vertexNum) {
1192
    return h3ToString(h3Api.cellToVertex(stringToH3(h3Address), vertexNum));
1✔
1193
  }
1194

1195
  public List<Long> cellToVertexes(long h3) {
1196
    long[] results = new long[6];
1✔
1197
    h3Api.cellToVertexes(h3, results);
1✔
1198
    return nonZeroLongArrayToList(results);
1✔
1199
  }
1200

1201
  public List<String> cellToVertexes(String h3Address) {
1202
    return h3ToStringList(cellToVertexes(stringToH3(h3Address)));
1✔
1203
  }
1204

1205
  public LatLng vertexToLatLng(long h3) {
1206
    double[] results = new double[2];
1✔
1207
    h3Api.vertexToLatLng(h3, results);
1✔
1208
    return new LatLng(toDegrees(results[0]), toDegrees(results[1]));
1✔
1209
  }
1210

1211
  public LatLng vertexToLatLng(String h3Address) {
1212
    return vertexToLatLng(stringToH3(h3Address));
1✔
1213
  }
1214

1215
  public boolean isValidVertex(long h3) {
1216
    return h3Api.isValidVertex(h3);
1✔
1217
  }
1218

1219
  public boolean isValidVertex(String h3Address) {
1220
    return h3Api.isValidVertex(stringToH3(h3Address));
1✔
1221
  }
1222

1223
  /**
1224
   * Returns the position of the child cell within an ordered list of all children of the cell's
1225
   * parent at the specified resolution parentRes.
1226
   */
1227
  public long cellToChildPos(String childAddress, int parentRes) {
1228
    return cellToChildPos(stringToH3(childAddress), parentRes);
1✔
1229
  }
1230

1231
  /**
1232
   * Returns the position of the child cell within an ordered list of all children of the cell's
1233
   * parent at the specified resolution parentRes.
1234
   */
1235
  public long cellToChildPos(long child, int parentRes) {
1236
    return h3Api.cellToChildPos(child, parentRes);
1✔
1237
  }
1238

1239
  /**
1240
   * Returns the child cell at a given position within an ordered list of all children of parent at
1241
   * the specified resolution childRes.
1242
   */
1243
  public long childPosToCell(long childPos, long parent, int childRes) {
1244
    return h3Api.childPosToCell(childPos, parent, childRes);
1✔
1245
  }
1246

1247
  /**
1248
   * Returns the child cell at a given position within an ordered list of all children of parent at
1249
   * the specified resolution childRes.
1250
   */
1251
  public String childPosToCell(long childPos, String parentAddress, int childRes) {
1252
    return h3ToString(childPosToCell(childPos, stringToH3(parentAddress), childRes));
1✔
1253
  }
1254

1255
  /** Transforms a collection of H3 indexes in string form to a list of H3 indexes in long form. */
1256
  private List<Long> stringToH3List(Collection<String> collection) {
1257
    return collection.stream().map(this::stringToH3).collect(Collectors.toList());
1✔
1258
  }
1259

1260
  /** Transforms a list of H3 indexes in long form to a list of H3 indexes in string form. */
1261
  private List<String> h3ToStringList(Collection<Long> collection) {
1262
    return collection.stream().map(this::h3ToString).collect(Collectors.toList());
1✔
1263
  }
1264

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

1269
    for (int i = 0; i < out.length; i++) {
1✔
1270
      long h = out[i];
1✔
1271
      if (h != 0) {
1✔
1272
        ret.add(h);
1✔
1273
      }
1274
    }
1275

1276
    return ret;
1✔
1277
  }
1278

1279
  /** Returns an array of <code>long</code> with the contents of the collection. */
1280
  private static long[] collectionToLongArray(Collection<Long> collection) {
1281
    return collection.stream().mapToLong(Long::longValue).toArray();
1✔
1282
  }
1283

1284
  /**
1285
   * @throws IllegalArgumentException <code>res</code> is not a valid H3 resolution.
1286
   */
1287
  private static void checkResolution(int res) {
1288
    if (res < 0 || res > 15) {
1✔
1289
      throw new IllegalArgumentException(
1✔
1290
          String.format("resolution %d is out of range (must be 0 <= res <= 15)", res));
1✔
1291
    }
1292
  }
1✔
1293

1294
  /**
1295
   * @throws IllegalArgumentException <code>sz</code> cannot be losslessly cast to an <code>int
1296
   *     </code>
1297
   */
1298
  private static int longToIntSize(long sz) {
1299
    if (sz < 0 || sz > Integer.MAX_VALUE) {
1!
1300
      throw new IllegalArgumentException(String.format("size %d is out of range", sz));
1✔
1301
    }
1302
    return (int) sz;
1✔
1303
  }
1304
}
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

© 2025 Coveralls, Inc