diff --git a/README.md b/README.md index 9725965..801fdbd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Coverage Status](https://coveralls.io/repos/github/uber/h3-java/badge.svg?branch=master)](https://coveralls.io/github/uber/h3-java?branch=master) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.uber/h3/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.uber/h3) -[![H3 Version](https://img.shields.io/badge/h3-v4.1.0-blue.svg)](https://github.com/uber/h3/releases/tag/v4.1.0) +[![H3 Version](https://img.shields.io/badge/h3-v4.2.0-blue.svg)](https://github.com/uber/h3/releases/tag/v4.2.0) This library provides Java bindings for the [H3 Core Library](https://github.com/uber/h3). For API reference, please see the [H3 Documentation](https://h3geo.org/). @@ -18,14 +18,14 @@ Add it to your pom.xml: com.uber h3 - 4.1.2 + 4.2.0 ``` Or, using Gradle: ```gradle -compile("com.uber:h3:4.1.2") +compile("com.uber:h3:4.2.0") ``` Encode a location into a hexagon address: diff --git a/h3version.properties b/h3version.properties index 3c21f6f..31fe3de 100644 --- a/h3version.properties +++ b/h3version.properties @@ -1 +1 @@ -h3.git.reference=v4.1.0 +h3.git.reference=v4.2.0 diff --git a/src/main/c/h3-java/build-h3-docker.sh b/src/main/c/h3-java/build-h3-docker.sh index ea33597..08c0cb8 100755 --- a/src/main/c/h3-java/build-h3-docker.sh +++ b/src/main/c/h3-java/build-h3-docker.sh @@ -29,6 +29,7 @@ BUILD_ROOT=$1 UPGRADE_CMAKE=$2 CMAKE_ROOT=$3 +# TODO: This may no longer be necessary if $UPGRADE_CMAKE; then pushd "$CMAKE_ROOT" if ! [ -e cmake-3.23.2-linux-x86_64.sh ]; then @@ -51,6 +52,7 @@ mkdir -p build pushd build cmake -DBUILD_SHARED_LIBS=OFF \ + -DENABLE_WARNINGS=OFF \ -DCMAKE_C_STANDARD_REQUIRED=ON \ -DCMAKE_C_STANDARD=99 \ -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ diff --git a/src/main/c/h3-java/build-h3.sh b/src/main/c/h3-java/build-h3.sh index a041ce8..cfa55ff 100755 --- a/src/main/c/h3-java/build-h3.sh +++ b/src/main/c/h3-java/build-h3.sh @@ -184,7 +184,7 @@ CMAKE_ROOT=target/cmake-3.23.2-linux-x86_64 mkdir -p $CMAKE_ROOT DOCKCROSS_IMAGES="android-arm android-arm64 linux-arm64 linux-armv5 linux-armv7 linux-s390x linux-ppc64le linux-x64 linux-x86 windows-static-x64 windows-static-x86" -if ! $DOCKCROSS_ONLY; then +if ! [ -z $DOCKCROSS_ONLY ]; then DOCKCROSS_IMAGES=$DOCKCROSS_ONLY echo Building only: $DOCKCROSS_IMAGES fi diff --git a/src/main/c/h3-java/src/jniapi.c b/src/main/c/h3-java/src/jniapi.c index 76c5762..61c4940 100644 --- a/src/main/c/h3-java/src/jniapi.c +++ b/src/main/c/h3-java/src/jniapi.c @@ -571,6 +571,32 @@ Java_com_uber_h3core_NativeMethods_maxPolygonToCellsSize( return numHexagons; } +/* + * Class: com_uber_h3core_NativeMethods + * Method: maxPolygonToCellsSizeExperimental + * Signature: ([D[I[DII)J + */ +JNIEXPORT jlong JNICALL +Java_com_uber_h3core_NativeMethods_maxPolygonToCellsSizeExperimental( + JNIEnv *env, jobject thiz, jdoubleArray verts, jintArray holeSizes, + jdoubleArray holeVerts, jint res, jint flags) { + GeoPolygon polygon; + if (CreateGeoPolygon(env, verts, holeSizes, holeVerts, &polygon)) { + return -1; + } + + jlong numHexagons; + H3Error err = + maxPolygonToCellsSizeExperimental(&polygon, res, flags, &numHexagons); + + DestroyGeoPolygon(env, verts, holeSizes, holeVerts, &polygon); + + if (err) { + ThrowH3Exception(env, err); + } + return numHexagons; +} + /* * Class: com_uber_h3core_NativeMethods * Method: getRes0Cells @@ -657,6 +683,40 @@ JNIEXPORT void JNICALL Java_com_uber_h3core_NativeMethods_polygonToCells( } } +/* + * Class: com_uber_h3core_NativeMethods + * Method: polygonToCellsExperimental + * Signature: ([D[I[DII[J)V + */ +JNIEXPORT void JNICALL +Java_com_uber_h3core_NativeMethods_polygonToCellsExperimental( + JNIEnv *env, jobject thiz, jdoubleArray verts, jintArray holeSizes, + jdoubleArray holeVerts, jint res, jint flags, jlongArray results) { + GeoPolygon polygon; + if (CreateGeoPolygon(env, verts, holeSizes, holeVerts, &polygon)) { + return; + } + + jlong *resultsElements = (**env).GetLongArrayElements(env, results, 0); + jsize resultsSize = (**env).GetArrayLength(env, results); + + H3Error err; + if (resultsElements != NULL) { + err = polygonToCellsExperimental(&polygon, res, flags, resultsSize, + resultsElements); + + (**env).ReleaseLongArrayElements(env, results, resultsElements, 0); + } else { + ThrowOutOfMemoryError(env); + } + + DestroyGeoPolygon(env, verts, holeSizes, holeVerts, &polygon); + + if (err) { + ThrowH3Exception(env, err); + } +} + /** * Converts the given polygon to managed objects * (ArrayList>>) @@ -1337,7 +1397,7 @@ JNIEXPORT jlong JNICALL Java_com_uber_h3core_NativeMethods_cellToVertex( JNIEXPORT void JNICALL Java_com_uber_h3core_NativeMethods_cellToVertexes( JNIEnv *env, jobject thiz, jlong h3, jlongArray vertexes) { jsize sz = (**env).GetArrayLength(env, vertexes); - jint *vertexesElements = (**env).GetLongArrayElements(env, vertexes, 0); + jlong *vertexesElements = (**env).GetLongArrayElements(env, vertexes, 0); if (vertexesElements != NULL && sz >= 6) { H3Error err = cellToVertexes(h3, vertexesElements); diff --git a/src/main/java/com/uber/h3core/H3Core.java b/src/main/java/com/uber/h3core/H3Core.java index d1b58de..3b24326 100644 --- a/src/main/java/com/uber/h3core/H3Core.java +++ b/src/main/java/com/uber/h3core/H3Core.java @@ -518,10 +518,68 @@ public List gridPathCells(long start, long end) { } /** - * Finds indexes within the given geofence. + * Finds indexes within the given geopolygon. * - * @param points Outline geofence - * @param holes Geofences of any internal holes + * @param points Outline geopolygon + * @param holes Geopolygons of any internal holes + * @param res Resolution of the desired indexes + */ + public List polygonToCellAddressesExperimental( + List points, List> holes, PolygonToCellsFlags flags, int res) { + return h3ToStringList(polygonToCellsExperimental(points, holes, flags, res)); + } + + /** + * Finds indexes within the given geopolygon. + * + * @param points Outline geopolygon + * @param holes Geopolygon of any internal holes + * @param res Resolution of the desired indexes + * @throws IllegalArgumentException Invalid resolution + */ + public List polygonToCellsExperimental( + List points, List> holes, PolygonToCellsFlags flags, int res) { + checkResolution(res); + + // pack the data for use by the polyfill JNI call + double[] verts = new double[points.size() * 2]; + packGeofenceVertices(verts, points, 0); + int[] holeSizes = new int[0]; + double[] holeVerts = new double[0]; + if (holes != null) { + int holesSize = holes.size(); + holeSizes = new int[holesSize]; + int totalSize = 0; + for (int i = 0; i < holesSize; i++) { + int holeSize = holes.get(i).size() * 2; + totalSize += holeSize; + // Note we are storing the number of doubles + holeSizes[i] = holeSize; + } + holeVerts = new double[totalSize]; + int offset = 0; + for (int i = 0; i < holesSize; i++) { + offset = packGeofenceVertices(holeVerts, holes.get(i), offset); + } + } + + int flagsInt = flags.toInt(); + int sz = + longToIntSize( + h3Api.maxPolygonToCellsSizeExperimental(verts, holeSizes, holeVerts, res, flagsInt)); + + long[] results = new long[sz]; + + h3Api.polygonToCellsExperimental(verts, holeSizes, holeVerts, res, flagsInt, results); + + return nonZeroLongArrayToList(results); + } + + /** + * Finds indexes within the given geopolygon. + * + * @param points Outline geopolygon + * @param holes Geopolygons of any internal holes * @param res Resolution of the desired indexes */ public List polygonToCellAddresses( @@ -530,10 +588,10 @@ public List polygonToCellAddresses( } /** - * Finds indexes within the given geofence. + * Finds indexes within the given geopolygon. * - * @param points Outline geofence - * @param holes Geofences of any internal holes + * @param points Outline geopolygon + * @param holes Geopolygon of any internal holes * @param res Resolution of the desired indexes * @throws IllegalArgumentException Invalid resolution */ diff --git a/src/main/java/com/uber/h3core/NativeMethods.java b/src/main/java/com/uber/h3core/NativeMethods.java index 92547ac..42c53fb 100644 --- a/src/main/java/com/uber/h3core/NativeMethods.java +++ b/src/main/java/com/uber/h3core/NativeMethods.java @@ -66,6 +66,12 @@ final class NativeMethods { native void gridPathCells(long start, long end, long[] results); + native long maxPolygonToCellsSizeExperimental( + double[] verts, int[] holeSizes, double[] holeVerts, int res, int flags); + + native void polygonToCellsExperimental( + double[] verts, int[] holeSizes, double[] holeVerts, int res, int flags, long[] results); + native long maxPolygonToCellsSize( double[] verts, int[] holeSizes, double[] holeVerts, int res, int flags); diff --git a/src/main/java/com/uber/h3core/PolygonToCellsFlags.java b/src/main/java/com/uber/h3core/PolygonToCellsFlags.java new file mode 100644 index 0000000..7c6bdd7 --- /dev/null +++ b/src/main/java/com/uber/h3core/PolygonToCellsFlags.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uber.h3core; + +/** Flags for polygonToCellsExperimental */ +public enum PolygonToCellsFlags { + /** Cell center is contained in the shape */ + containment_center(0), + /** Cell is fully contained in the shape */ + containment_full(1), + /** Cell overlaps the shape at any point */ + containment_overlapping(2), + /** Cell bounding box overlaps shape */ + containment_overlapping_bbox(3); + + private final int value; + + PolygonToCellsFlags(int value) { + this.value = value; + } + + public int toInt() { + return this.value; + } +} diff --git a/src/test/java/com/uber/h3core/TestBindingCompleteness.java b/src/test/java/com/uber/h3core/TestBindingCompleteness.java index bd401ae..810a4b4 100644 --- a/src/test/java/com/uber/h3core/TestBindingCompleteness.java +++ b/src/test/java/com/uber/h3core/TestBindingCompleteness.java @@ -33,7 +33,10 @@ class TestBindingCompleteness { private static final Set WHITELIST = ImmutableSet.of( // These are provided by the Java library (java.lang.Math) - "degsToRads", "radsToDegs"); + "degsToRads", + "radsToDegs", + // Handled by H3Exception + "describeH3Error"); @Test @DisabledInNativeImage diff --git a/src/test/java/com/uber/h3core/TestRegion.java b/src/test/java/com/uber/h3core/TestRegion.java index c00869e..fac83d8 100644 --- a/src/test/java/com/uber/h3core/TestRegion.java +++ b/src/test/java/com/uber/h3core/TestRegion.java @@ -29,6 +29,96 @@ /** Tests for region (polyfill, h3SetToMultiPolygon) functions. */ class TestRegion extends BaseTestH3Core { + @Test + void polyfillExperimentalCenter() { + List hexagons = + h3.polygonToCellsExperimental( + ImmutableList.of( + new LatLng(37.813318999983238, -122.4089866999972145), + new LatLng(37.7866302000007224, -122.3805436999997056), + new LatLng(37.7198061999978478, -122.3544736999993603), + new LatLng(37.7076131999975672, -122.5123436999983966), + new LatLng(37.7835871999971715, -122.5247187000021967), + new LatLng(37.8151571999998453, -122.4798767000009008)), + null, + PolygonToCellsFlags.containment_center, + 9); + + assertTrue(hexagons.size() > 1000); + } + + @Test + void polyfillExperimentalFull() { + List hexagons = + h3.polygonToCellsExperimental( + ImmutableList.of( + new LatLng(37.813318999983238, -122.4089866999972145), + new LatLng(37.7866302000007224, -122.3805436999997056), + new LatLng(37.7198061999978478, -122.3544736999993603), + new LatLng(37.7076131999975672, -122.5123436999983966), + new LatLng(37.7835871999971715, -122.5247187000021967), + new LatLng(37.8151571999998453, -122.4798767000009008)), + null, + PolygonToCellsFlags.containment_full, + 9); + + assertTrue(hexagons.size() > 1000); + } + + @Test + void polyfillExperimentalOverlapping() { + List hexagons = + h3.polygonToCellsExperimental( + ImmutableList.of( + new LatLng(37.813318999983238, -122.4089866999972145), + new LatLng(37.7866302000007224, -122.3805436999997056), + new LatLng(37.7198061999978478, -122.3544736999993603), + new LatLng(37.7076131999975672, -122.5123436999983966), + new LatLng(37.7835871999971715, -122.5247187000021967), + new LatLng(37.8151571999998453, -122.4798767000009008)), + null, + PolygonToCellsFlags.containment_overlapping, + 9); + + assertTrue(hexagons.size() > 1000); + } + + @Test + void polyfillExperimentalOverlappingBbox() { + List hexagons = + h3.polygonToCellsExperimental( + ImmutableList.of( + new LatLng(37.813318999983238, -122.4089866999972145), + new LatLng(37.7866302000007224, -122.3805436999997056), + new LatLng(37.7198061999978478, -122.3544736999993603), + new LatLng(37.7076131999975672, -122.5123436999983966), + new LatLng(37.7835871999971715, -122.5247187000021967), + new LatLng(37.8151571999998453, -122.4798767000009008)), + null, + PolygonToCellsFlags.containment_overlapping_bbox, + 9); + + assertTrue(hexagons.size() > 1000); + } + + @Test + void polyfillExperimental() { + List hexagons = + h3.polygonToCellsExperimental( + ImmutableList.of( + new LatLng(37.813318999983238, -122.4089866999972145), + new LatLng(37.7866302000007224, -122.3805436999997056), + new LatLng(37.7198061999978478, -122.3544736999993603), + new LatLng(37.7076131999975672, -122.5123436999983966), + new LatLng(37.7835871999971715, -122.5247187000021967), + new LatLng(37.8151571999998453, -122.4798767000009008)), + null, + PolygonToCellsFlags.containment_center, + 9); + + assertTrue(hexagons.size() > 1000); + } + @Test void polyfill() { List hexagons =