You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2026/10/17 05:22:23 UTC
(sedona) branch master updated: [GH-3020] Add ST_H3ToChildren (#3470)
This is an automated email from the ASF dual-hosted git repository.
jiayuasu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new bd17bc8e1a7 [GH-3020] Add ST_H3ToChildren (#3470)
bd17bc8e1a7 is described below
commit bd17bc8e1a774d9c03750d12466ef479ac47f496
Author: Simen Omholt-Jensen <41...@users.noreply.github.com>
AuthorDate: Sat Oct 17 07:22:16 2026 +0200
[GH-3020] Add ST_H3ToChildren (#3470)
---
.../java/org/apache/sedona/common/Functions.java | 11 +++++
.../org/apache/sedona/common/FunctionsTest.java | 42 ++++++++++++++++
docs/api/flink/Geometry-Functions.md | 1 +
docs/api/flink/Spatial-Indexing/ST_H3ToChildren.md | 56 ++++++++++++++++++++++
docs/api/sql/Geometry-Functions.md | 1 +
docs/api/sql/Spatial-Indexing/ST_H3ToChildren.md | 56 ++++++++++++++++++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 1 +
.../apache/sedona/flink/expressions/Functions.java | 11 +++++
.../java/org/apache/sedona/flink/FunctionTest.java | 40 ++++++++++++++++
python/sedona/spark/sql/st_functions.py | 14 ++++++
python/tests/sql/test_dataframe_api.py | 12 +++++
python/tests/sql/test_function.py | 21 ++++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 8 ++++
.../sql/sedona_sql/expressions/st_functions.scala | 9 ++++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 19 ++++++++
.../sedona/sql/functions/STH3Functions.scala | 41 ++++++++++++++++
17 files changed, 344 insertions(+)
diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java b/common/src/main/java/org/apache/sedona/common/Functions.java
index 7067297739b..b67a56ee82a 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -2077,6 +2077,17 @@ public class Functions {
return H3Utils.h3.cellToParent(cell, resolution);
}
+ /**
+ * Returns the children of an H3 cell at the requested resolution.
+ *
+ * @param cell the H3 cell
+ * @param resolution the resolution of the child cells, between the input cell's resolution and 15
+ * @return the child H3 cells, or the input cell when the resolutions are equal
+ */
+ public static Long[] h3ToChildren(long cell, int resolution) {
+ return H3Utils.h3.cellToChildren(cell, resolution).toArray(new Long[0]);
+ }
+
/**
* gets the polygon for each h3 index provided
*
diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
index a94978d2516..aebd72051c7 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.*;
import com.google.common.geometry.S2CellId;
import com.google.common.math.DoubleMath;
+import com.uber.h3core.exceptions.H3Exception;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.sedona.common.geometryObjects.Box2D;
@@ -1608,6 +1609,47 @@ public class FunctionsTest extends TestBase {
assertEquals(child, Functions.h3ToParent(child, 8));
}
+ @Test
+ public void h3ToChildren() {
+ long parent = H3Utils.coordinateToCell(new Coordinate(1, 2), 5);
+ for (int resolution : new int[] {6, 7}) {
+ Long[] children = Functions.h3ToChildren(parent, resolution);
+ assertEquals(resolution == 6 ? 7 : 49, children.length);
+ assertEquals(children.length, new HashSet<>(Arrays.asList(children)).size());
+ for (long child : children) {
+ assertEquals(resolution, H3Utils.h3.getResolution(child));
+ assertEquals(parent, Functions.h3ToParent(child, 5));
+ }
+ }
+ for (int resolution : new int[] {0, 5, 15}) {
+ long cell = H3Utils.coordinateToCell(new Coordinate(1, 2), resolution);
+ assertArrayEquals(new Long[] {cell}, Functions.h3ToChildren(cell, resolution));
+ }
+ }
+
+ @Test
+ public void h3ToChildrenPentagon() {
+ long parent = H3Utils.h3.getPentagons(5).iterator().next();
+ for (int resolution : new int[] {6, 7}) {
+ Long[] children = Functions.h3ToChildren(parent, resolution);
+ assertEquals(resolution == 6 ? 6 : 41, children.length);
+ assertEquals(children.length, new HashSet<>(Arrays.asList(children)).size());
+ for (long child : children) {
+ assertEquals(resolution, H3Utils.h3.getResolution(child));
+ assertEquals(parent, Functions.h3ToParent(child, 5));
+ }
+ }
+ }
+
+ @Test
+ public void h3ToChildrenInvalidResolution() {
+ long cell = H3Utils.coordinateToCell(new Coordinate(1, 2), 5);
+ for (int resolution : new int[] {-1, 16}) {
+ assertThrows(IllegalArgumentException.class, () -> Functions.h3ToChildren(cell, resolution));
+ }
+ assertThrows(H3Exception.class, () -> Functions.h3ToChildren(cell, 4));
+ }
+
@Test
public void geometricMedian() throws Exception {
MultiPoint multiPoint =
diff --git a/docs/api/flink/Geometry-Functions.md b/docs/api/flink/Geometry-Functions.md
index f00ee5c171d..c88821a65d5 100644
--- a/docs/api/flink/Geometry-Functions.md
+++ b/docs/api/flink/Geometry-Functions.md
@@ -350,6 +350,7 @@ These functions work with spatial indexing systems including Bing Tiles, H3, Hil
| [ST_H3CellDistance](Spatial-Indexing/ST_H3CellDistance.md) | Long | return result of h3 function [gridDistance(cel1, cell2)](https://h3geo.org/docs/api/traversal#griddistance). As described by H3 documentation > Finding the distance can fail because the two indexes... | v1.5.0 |
| [ST_H3CellIDs](Spatial-Indexing/ST_H3CellIDs.md) | `Array<Long>` | Cover the geometry by H3 cell IDs with the given resolution(level). To understand the cell statistics please refer to [H3 Doc](https://h3geo.org/docs/core-library/restable) H3 native fill functions... | v1.5.0 |
| [ST_H3KRing](Spatial-Indexing/ST_H3KRing.md) | `Array<Long>` | return the result of H3 function [gridDisk(cell, k)](https://h3geo.org/docs/api/traversal#griddisk). | v1.5.0 |
+| [ST_H3ToChildren](Spatial-Indexing/ST_H3ToChildren.md) | `Array<Long>` | Returns the children of an H3 cell at the requested resolution. | v2.0.0 |
| [ST_H3ToGeom](Spatial-Indexing/ST_H3ToGeom.md) | `Array<Geometry>` | Return the result of H3 function [cellsToMultiPolygon(cells)](https://h3geo.org/docs/api/regions#cellstolinkedmultipolygon--cellstomultipolygon). | v1.6.0 |
| [ST_H3ToParent](Spatial-Indexing/ST_H3ToParent.md) | Long | Returns the parent of an H3 cell at the requested resolution. | v2.0.0 |
| [ST_HilbertDistance](Spatial-Indexing/ST_HilbertDistance.md) | Long | Maps the midpoint of a geometry's envelope to an unsigned Hilbert-curve address within a supplied extent. | v2.0.0 |
diff --git a/docs/api/flink/Spatial-Indexing/ST_H3ToChildren.md b/docs/api/flink/Spatial-Indexing/ST_H3ToChildren.md
new file mode 100644
index 00000000000..2da0e3add7a
--- /dev/null
+++ b/docs/api/flink/Spatial-Indexing/ST_H3ToChildren.md
@@ -0,0 +1,56 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+ -->
+
+# ST_H3ToChildren
+
+Introduction: Returns the children of an H3 cell at the requested resolution using H3's [cellToChildren(cell, resolution)](https://h3geo.org/docs/api/hierarchy#celltochildren).
+
+The requested resolution must be between the input cell's resolution and 15, inclusive. An equal resolution returns an array containing only the input cell. A lower resolution or a resolution outside this range raises an error. A null cell or resolution returns null.
+
+The result contains the cell's descendants in the H3 hierarchy. An immediate hexagon parent has seven children, while an immediate pentagon parent has six. The order of the returned cells is not guaranteed.
+
+Format: `ST_H3ToChildren(cell: Long, resolution: Int)`
+
+Return type: `Array<Long>`
+
+Since: `v2.0.0`
+
+SQL Example
+
+```sql
+SELECT ST_H3ToChildren(601041811137363967, 5)
+```
+
+Output:
+
+```text
+[601041811137363967]
+```
+
+To count the immediate children of this resolution 5 hexagon:
+
+```sql
+SELECT CARDINALITY(ST_H3ToChildren(601041811137363967, 6)) AS child_count
+```
+
+Output:
+
+```text
+7
+```
diff --git a/docs/api/sql/Geometry-Functions.md b/docs/api/sql/Geometry-Functions.md
index 6c286d6fbb3..ccbf2c9affd 100644
--- a/docs/api/sql/Geometry-Functions.md
+++ b/docs/api/sql/Geometry-Functions.md
@@ -358,6 +358,7 @@ These functions work with spatial indexing systems including Bing Tiles, H3, Hil
| [ST_H3CellDistance](Spatial-Indexing/ST_H3CellDistance.md) | Long | return result of h3 function [gridDistance(cel1, cell2)](https://h3geo.org/docs/api/traversal#griddistance). As described by H3 documentation > Finding the distance can fail because the two indexes... | v1.5.0 |
| [ST_H3CellIDs](Spatial-Indexing/ST_H3CellIDs.md) | `Array<Long>` | Cover the geometry by H3 cell IDs with the given resolution(level). To understand the cell statistics please refer to [H3 Doc](https://h3geo.org/docs/core-library/restable) H3 native fill functions... | v1.5.0 |
| [ST_H3KRing](Spatial-Indexing/ST_H3KRing.md) | `Array<Long>` | return the result of H3 function [gridDisk(cell, k)](https://h3geo.org/docs/api/traversal#griddisk). | v1.5.0 |
+| [ST_H3ToChildren](Spatial-Indexing/ST_H3ToChildren.md) | `Array<Long>` | Returns the children of an H3 cell at the requested resolution. | v2.0.0 |
| [ST_H3ToGeom](Spatial-Indexing/ST_H3ToGeom.md) | `Array<Geometry>` | Return the result of H3 function [cellsToMultiPolygon(cells)](https://h3geo.org/docs/api/regions#cellstolinkedmultipolygon--cellstomultipolygon). | v1.6.0 |
| [ST_H3ToParent](Spatial-Indexing/ST_H3ToParent.md) | Long | Returns the parent of an H3 cell at the requested resolution. | v2.0.0 |
| [ST_HilbertDistance](Spatial-Indexing/ST_HilbertDistance.md) | Long | Maps the midpoint of a geometry's envelope to an unsigned Hilbert-curve address within a supplied extent. | v2.0.0 |
diff --git a/docs/api/sql/Spatial-Indexing/ST_H3ToChildren.md b/docs/api/sql/Spatial-Indexing/ST_H3ToChildren.md
new file mode 100644
index 00000000000..a6818479bda
--- /dev/null
+++ b/docs/api/sql/Spatial-Indexing/ST_H3ToChildren.md
@@ -0,0 +1,56 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+ -->
+
+# ST_H3ToChildren
+
+Introduction: Returns the children of an H3 cell at the requested resolution using H3's [cellToChildren(cell, resolution)](https://h3geo.org/docs/api/hierarchy#celltochildren).
+
+The requested resolution must be between the input cell's resolution and 15, inclusive. An equal resolution returns an array containing only the input cell. A lower resolution or a resolution outside this range raises an error. A null cell or resolution returns null.
+
+The result contains the cell's descendants in the H3 hierarchy. An immediate hexagon parent has seven children, while an immediate pentagon parent has six. The order of the returned cells is not guaranteed.
+
+Format: `ST_H3ToChildren(cell: Long, resolution: Int)`
+
+Return type: `Array<Long>`
+
+Since: `v2.0.0`
+
+SQL Example
+
+```sql
+SELECT ST_H3ToChildren(601041811137363967, 5)
+```
+
+Output:
+
+```text
+[601041811137363967]
+```
+
+To count the immediate children of this resolution 5 hexagon:
+
+```sql
+SELECT SIZE(ST_H3ToChildren(601041811137363967, 6)) AS child_count
+```
+
+Output:
+
+```text
+7
+```
diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
index ea8c1ef7ed4..ab7270ec3d8 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -111,6 +111,7 @@ public class Catalog {
new Functions.ST_H3KRing(),
new Functions.ST_H3ToGeom(),
new Functions.ST_H3ToParent(),
+ new Functions.ST_H3ToChildren(),
new Functions.ST_HilbertDistance(),
new Functions.ST_BingTile(),
new Functions.ST_BingTileAt(),
diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
index 52daad75f3d..80ad4a9d7c3 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
@@ -2963,6 +2963,17 @@ public class Functions {
}
}
+ public static class ST_H3ToChildren extends ScalarFunction {
+ @DataTypeHint(value = "ARRAY<BIGINT>")
+ public Long[] eval(
+ @DataTypeHint("BIGINT") Long cell, @DataTypeHint("INTEGER") Integer resolution) {
+ if (cell == null || resolution == null) {
+ return null;
+ }
+ return org.apache.sedona.common.Functions.h3ToChildren(cell, resolution);
+ }
+ }
+
// =========================================================================
// Bing Tile functions
// =========================================================================
diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
index d3696180436..4109434724c 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -23,6 +23,7 @@ import static org.apache.flink.table.api.Expressions.*;
import static org.junit.Assert.*;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -2381,6 +2382,45 @@ public class FunctionTest extends TestBase {
assertNull(first(nullTable).getField(0));
}
+ @Test
+ public void testH3ToChildren() {
+ long parent = H3Utils.coordinateToCell(new Coordinate(1, 2), 5);
+ Long[] children =
+ (Long[]) first(tableEnv.sqlQuery("SELECT ST_H3ToChildren(" + parent + ", 7)")).getField(0);
+ assertNotNull(children);
+ assertEquals(49, children.length);
+ assertEquals(49, new HashSet<>(Arrays.asList(children)).size());
+ for (long child : children) {
+ assertEquals(7, H3Utils.h3.getResolution(child));
+ assertEquals(parent, H3Utils.h3.cellToParent(child, 5));
+ }
+ assertArrayEquals(
+ new Long[] {parent},
+ (Long[]) first(tableEnv.sqlQuery("SELECT ST_H3ToChildren(" + parent + ", 5)")).getField(0));
+
+ long pentagon = H3Utils.h3.getPentagons(5).iterator().next();
+ Long[] pentagonChildren =
+ (Long[])
+ first(tableEnv.sqlQuery("SELECT ST_H3ToChildren(" + pentagon + ", 6)")).getField(0);
+ assertNotNull(pentagonChildren);
+ assertEquals(6, pentagonChildren.length);
+ for (long child : pentagonChildren) {
+ assertEquals(pentagon, H3Utils.h3.cellToParent(child, 5));
+ }
+
+ Row nulls =
+ first(
+ tableEnv.sqlQuery(
+ "SELECT ST_H3ToChildren(CAST(NULL AS BIGINT), 6), "
+ + "ST_H3ToChildren("
+ + parent
+ + ", CAST(NULL AS INT)), "
+ + "ST_H3ToChildren(CAST(NULL AS BIGINT), CAST(NULL AS INT))"));
+ for (int i = 0; i < 3; i++) {
+ assertNull(nulls.getField(i));
+ }
+ }
+
@Test
public void testGeometricMedian() throws ParseException {
Table pointTable =
diff --git a/python/sedona/spark/sql/st_functions.py b/python/sedona/spark/sql/st_functions.py
index 05800bc7fad..c3c15b1b638 100644
--- a/python/sedona/spark/sql/st_functions.py
+++ b/python/sedona/spark/sql/st_functions.py
@@ -1016,6 +1016,20 @@ def ST_H3ToParent(
return _call_st_function("ST_H3ToParent", args)
+@validate_argument_types
+def ST_H3ToChildren(
+ cell: Union[ColumnOrName, int], resolution: Union[ColumnOrName, int]
+) -> Column:
+ """Return the children of an H3 cell at the requested resolution.
+ :param cell: H3 cell
+ :param resolution: resolution of the child cells, from the input resolution to 15
+ :return: child H3 cells, or the input cell when the resolutions are equal
+ :rtype: List[long]
+ """
+ args = (cell, resolution)
+ return _call_st_function("ST_H3ToChildren", args)
+
+
# =========================================================================
# Bing Tile functions
# =========================================================================
diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py
index 323dbedb92d..7c46cbae2da 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -2009,6 +2009,18 @@ class TestDataFrameAPI(TestBase):
parent = df.select(stf.ST_H3ToParent("cell", 5)).first()[0]
assert parent == 601041811137363967
+ def test_st_h3_to_children(self):
+ parent = 601041811137363967
+ df = self.spark.createDataFrame([(parent, 6)], ["cell", "resolution"])
+ by_name, by_column, by_literal = df.select(
+ stf.ST_H3ToChildren("cell", 6),
+ stf.ST_H3ToChildren(f.col("cell"), f.col("resolution")),
+ stf.ST_H3ToChildren(parent, 6),
+ ).first()
+ assert len(by_name) == len(set(by_name)) == 7
+ assert by_name == by_column == by_literal
+ assert df.select(stf.ST_H3ToChildren("cell", 5)).first()[0] == [parent]
+
def test_coverage_validation_catalog_calls(self):
rows = self.spark.createDataFrame(
[
diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py
index d3657b38055..84131d8ccef 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -2440,6 +2440,27 @@ class TestPredicateJoin(TestBase):
).first()[0]
assert null_parent is None
+ def test_st_h3_to_children(self):
+ parent = 601041811137363967
+ children = self.spark.sql(f"SELECT ST_H3ToChildren({parent}, 6)").first()[0]
+ assert isinstance(children, list)
+ assert len(children) == len(set(children)) == 7
+ assert all(isinstance(child, int) for child in children)
+ parents = self.spark.createDataFrame([(child,) for child in children], ["cell"])
+ assert all(
+ row[0] == parent
+ for row in parents.selectExpr("ST_H3ToParent(cell, 5)").collect()
+ )
+ assert self.spark.sql(f"SELECT ST_H3ToChildren({parent}, 5)").first()[0] == [
+ parent
+ ]
+ nulls = self.spark.sql(f"""
+ SELECT ST_H3ToChildren(CAST(NULL AS BIGINT), 6),
+ ST_H3ToChildren({parent}, CAST(NULL AS INT)),
+ ST_H3ToChildren(CAST(NULL AS BIGINT), CAST(NULL AS INT))
+ """).first()
+ assert all(value is None for value in nulls)
+
def test_st_h3_togeom(self):
df = self.spark.sql("""
SELECT
diff --git a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index bd84a06026e..58373df5bf9 100644
--- a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -318,6 +318,7 @@ object Catalog extends AbstractCatalog with Logging {
function[ST_H3KRing](),
function[ST_H3ToGeom](),
function[ST_H3ToParent](),
+ function[ST_H3ToChildren](),
function[ST_HilbertDistance](),
function[ST_S2CellIDs](),
function[ST_S2ToGeom](),
diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 1e24bd8ce3d..feeeea9ee0b 100644
--- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -1735,6 +1735,14 @@ private[apache] case class ST_H3ToParent(inputExpressions: Seq[Expression])
}
}
+private[apache] case class ST_H3ToChildren(inputExpressions: Seq[Expression])
+ extends InferredExpression(Functions.h3ToChildren _) {
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+ copy(inputExpressions = newChildren)
+ }
+}
+
// =========================================================================
// Bing Tile expressions
// =========================================================================
diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
index af295db4e2b..bdd410d266c 100644
--- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
+++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
@@ -311,6 +311,15 @@ object st_functions {
def ST_H3ToParent(cell: Long, resolution: Integer): Column =
wrapExpression[ST_H3ToParent](cell, resolution)
+ def ST_H3ToChildren(cell: Column, resolution: Column): Column =
+ wrapExpression[ST_H3ToChildren](cell, resolution)
+
+ def ST_H3ToChildren(cell: Column, resolution: Integer): Column =
+ wrapExpression[ST_H3ToChildren](cell, resolution)
+
+ def ST_H3ToChildren(cell: Long, resolution: Integer): Column =
+ wrapExpression[ST_H3ToChildren](cell, resolution)
+
def ST_HilbertDistance(
geometry: Column,
xmin: Column,
diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
index 34d8c9ca499..1f576a768b8 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
@@ -2097,6 +2097,25 @@ class dataFrameAPITestScala extends TestBaseScala {
assert(result.getLong(1) == 601041811137363967L)
}
+ it("Passed ST_H3ToChildren") {
+ val cell = 601041811137363967L
+ val result = sparkSession
+ .sql(s"SELECT $cell AS cell, 6 AS resolution")
+ .select(
+ ST_H3ToChildren(col("cell"), col("resolution")),
+ ST_H3ToChildren(col("cell"), 6),
+ ST_H3ToChildren(cell, 6))
+ .first()
+ val children = result.getSeq[Long](0)
+ assert(children.size == 7)
+ assert(children.distinct.size == 7)
+ assert(result.getSeq[Long](1) == children)
+ assert(result.getSeq[Long](2) == children)
+ children.foreach { child =>
+ assert(org.apache.sedona.common.Functions.h3ToParent(child, 5) == cell)
+ }
+ }
+
it("Passed ST_DistanceSphere") {
val baseDf = sparkSession.sql(
"SELECT ST_GeomFromWKT('POINT (0 0)') AS geom1, ST_GeomFromWKT('POINT (90 0)') AS geom2")
diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/functions/STH3Functions.scala b/spark/common/src/test/scala/org/apache/sedona/sql/functions/STH3Functions.scala
index 452eecf730b..3475d47dd3d 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/functions/STH3Functions.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/functions/STH3Functions.scala
@@ -144,6 +144,47 @@ class STH3Functions extends TestBaseScala with Matchers with GeometrySample with
exacts should equal(expects)
}
+ it("should return child cells at the requested resolution") {
+ val parent = H3Utils.coordinateToCell(new Coordinate(1, 2), 5)
+ val children = Seq((parent, 7))
+ .toDF("cell", "resolution")
+ .select(expr("ST_H3ToChildren(cell, resolution)"))
+ .first()
+ .getSeq[Long](0)
+
+ children.size should equal(49)
+ children.distinct.size should equal(49)
+ children.foreach { child =>
+ H3Utils.h3.getResolution(child) should equal(7)
+ Functions.h3ToParent(child, 5) should equal(parent)
+ }
+ sparkSession
+ .sql(s"SELECT ST_H3ToChildren($parent, 5)")
+ .first()
+ .getSeq[Long](0) should equal(Seq(parent))
+ }
+
+ it("should return six immediate children for a pentagon") {
+ val parent = H3Utils.h3.getPentagons(5).iterator().next().longValue()
+ val children = sparkSession
+ .sql(s"SELECT ST_H3ToChildren($parent, 6)")
+ .first()
+ .getSeq[Long](0)
+ children.size should equal(6)
+ children.foreach(child => Functions.h3ToParent(child, 5) should equal(parent))
+ }
+
+ it("should propagate null inputs to ST_H3ToChildren") {
+ val result = sparkSession
+ .sql("""SELECT
+ |ST_H3ToChildren(CAST(NULL AS BIGINT), 6),
+ |ST_H3ToChildren(601041811137363967, CAST(NULL AS INT)),
+ |ST_H3ToChildren(CAST(NULL AS BIGINT), CAST(NULL AS INT))
+ |""".stripMargin)
+ .first()
+ (0 until 3).foreach(i => result.isNullAt(i) should be(true))
+ }
+
it("should return the parent cell at the requested resolution") {
Given("DataFrame with a valid Geometry")
val geometryTable = Seq("POINT(1 2)").map(geom => Tuple1(wktReader.read(geom))).toDF("geom")