This is an automated email from the ASF dual-hosted git repository.
jiayu 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 7876d8c62d [SEDONA-686] Add ST_LabelPoint (#1712)
7876d8c62d is described below
commit 7876d8c62de1ce97b2d0392d4fc75d01c882fb35
Author: Pranav Toggi <[email protected]>
AuthorDate: Wed Dec 18 13:42:32 2024 +0530
[SEDONA-686] Add ST_LabelPoint (#1712)
* implement in java
* implement in scala
* implement in flink
* implement in python
* implement in snowflake
* update docs
* fix lint
* update function name; refactor docs
* support nested GeometryCollections
* use gridResolution instead of stepSize
* use gridResolution instead of stepSize
* fix test
* fix test
* fix test
* Revise documentation
revisions to increase the accuracy of ST_LabelPoint docs
* copy updates from Flink Docs
* copy updates from Flink Docs
* remove println
* add preserveSRID test
* fix snowflake UDF
* fix snowflake UDF
---------
Co-authored-by: James Willis <[email protected]>
---
.../java/org/apache/sedona/common/Functions.java | 117 +++++++++++++++++++++
.../org/apache/sedona/common/FunctionsTest.java | 35 ++++++
docs/api/flink/Function.md | 74 +++++++++++++
docs/api/snowflake/vector-data/Function.md | 72 +++++++++++++
docs/api/sql/Function.md | 74 +++++++++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 1 +
.../apache/sedona/flink/expressions/Functions.java | 27 +++++
.../java/org/apache/sedona/flink/FunctionTest.java | 30 ++++++
python/sedona/sql/st_functions.py | 30 ++++++
python/tests/sql/test_dataframe_api.py | 22 ++++
python/tests/sql/test_function.py | 19 ++++
.../sedona/snowflake/snowsql/TestFunctions.java | 19 ++++
.../sedona/snowflake/snowsql/TestFunctionsV2.java | 16 +++
.../org/apache/sedona/snowflake/snowsql/UDFs.java | 17 +++
.../apache/sedona/snowflake/snowsql/UDFsV2.java | 23 ++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 11 ++
.../sql/sedona_sql/expressions/st_functions.scala | 16 +++
.../org/apache/sedona/sql/PreserveSRIDSuite.scala | 1 +
.../apache/sedona/sql/dataFrameAPITestScala.scala | 32 ++++++
.../org/apache/sedona/sql/functionTestScala.scala | 30 ++++++
21 files changed, 667 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 6122169325..9929647b60 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -80,6 +80,123 @@ public class Functions {
return geometry.getArea();
}
+ public static Geometry labelPoint(Geometry geometry) {
+ return labelPoint(geometry, 16, 0.2);
+ }
+
+ public static Geometry labelPoint(Geometry geometry, int gridResolution) {
+ return labelPoint(geometry, gridResolution, 0.2);
+ }
+
+ public static Geometry labelPoint(
+ Geometry geometry, int gridResolution, double goodnessThreshold) {
+ if (geometry.getArea() <= 0) {
+ throw new IllegalArgumentException("Geometry must have a positive area");
+ }
+
+ GeometryFactory geometryFactory = new GeometryFactory();
+
+ // Find the largest polygon
+ Polygon largestPolygon = findLargestPolygon(geometry);
+
+ if (largestPolygon == null) {
+ throw new IllegalArgumentException("Geometry must contain at least one
Polygon");
+ }
+
+ return polygonToLabel(largestPolygon, gridResolution, goodnessThreshold,
geometryFactory);
+ }
+
+ private static Polygon findLargestPolygon(Geometry geometry) {
+ if (geometry instanceof Polygon) {
+ return (Polygon) geometry;
+ }
+
+ if (geometry instanceof GeometryCollection) {
+ GeometryCollection gc = (GeometryCollection) geometry;
+ Polygon largestPolygon = null;
+ double maxArea = Double.MIN_VALUE;
+
+ for (int i = 0; i < gc.getNumGeometries(); i++) {
+ Geometry subGeometry = gc.getGeometryN(i);
+ Polygon candidate = findLargestPolygon(subGeometry);
+
+ if (candidate != null && candidate.getArea() > maxArea) {
+ largestPolygon = candidate;
+ maxArea = candidate.getArea();
+ }
+ }
+ return largestPolygon;
+ }
+
+ return null;
+ }
+
+ private static Point polygonToLabel(
+ Polygon polygon,
+ int gridResolution,
+ double goodnessThreshold,
+ GeometryFactory geometryFactory) {
+ if (polygon.getArea() <= 0) {
+ throw new IllegalArgumentException("Polygon must have a positive area");
+ }
+
+ Envelope env = polygon.getEnvelopeInternal();
+ double xmin = env.getMinX();
+ double ymin = env.getMinY();
+ double xmax = env.getMaxX();
+ double ymax = env.getMaxY();
+
+ // Calculate step size based on grid resolution
+ double stepSizeX = (xmax - xmin) / gridResolution;
+ double stepSizeY = (ymax - ymin) / gridResolution;
+
+ Point centroid = polygon.getCentroid();
+ double radius = Math.sqrt(polygon.getArea() / Math.PI);
+ goodnessThreshold = radius * goodnessThreshold;
+
+ double bestGoodness = labelGoodness(polygon, centroid);
+
+ if (bestGoodness < goodnessThreshold) {
+ for (int x = 0; x < gridResolution; x++) {
+ for (int y = 0; y < gridResolution; y++) {
+ double candidateX = xmin + x * stepSizeX;
+ double candidateY = ymin + y * stepSizeY;
+ Point candidate = geometryFactory.createPoint(new
Coordinate(candidateX, candidateY));
+
+ double candidateGoodness = labelGoodness(polygon, candidate);
+
+ if (candidateGoodness > bestGoodness) {
+ centroid = candidate;
+ bestGoodness = candidateGoodness;
+ }
+ }
+ }
+ }
+
+ return centroid;
+ }
+
+ private static double labelGoodness(Geometry geometry, Point point) {
+ if (!geometry.intersects(point)) {
+ return 0.0;
+ }
+
+ double closest = Double.POSITIVE_INFINITY;
+ Coordinate[] coordinates = geometry.getCoordinates();
+
+ for (Coordinate coord : coordinates) {
+ double dx = coord.x - point.getX();
+ double dy = coord.y - point.getY();
+ double distanceSquared = dx * dx + dy * dy;
+
+ if (distanceSquared < closest) {
+ closest = distanceSquared;
+ }
+ }
+
+ return Math.sqrt(closest);
+ }
+
public static double azimuth(Geometry left, Geometry right) {
Coordinate leftCoordinate = left.getCoordinate();
Coordinate rightCoordinate = right.getCoordinate();
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 85efbf17e2..c34513e10d 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -63,6 +63,41 @@ public class FunctionsTest extends TestBase {
private final WKTReader wktReader = new WKTReader();
+ @Test
+ public void labelPoint() throws Exception {
+ Geometry geom =
+ Constructors.geomFromWKT(
+ "POLYGON ((-112.637484 33.440546, -112.546852 33.477209,
-112.489177 33.550488, -112.41777 33.751684, -111.956371 33.719707, -111.766868
33.616843, -111.775107 33.527595, -111.640533 33.504695, -111.440044 33.463462,
-111.415326 33.374055, -111.514197 33.309809, -111.643279 33.222542,
-111.893203 33.174278, -111.96461 33.250109, -112.123903 33.261593, -112.252985
33.35341, -112.406784 33.346527, -112.667694 33.316695, -112.637484
33.440546))",
+ 4326);
+ String labelPoint = Functions.asEWKT(Functions.labelPoint(geom));
+ String expected = "SRID=4326;POINT (-112.04278737349767
33.46420809489905)";
+ assertEquals(expected, labelPoint);
+
+ geom =
+ Constructors.geomFromWKT(
+ "GEOMETRYCOLLECTION(POLYGON ((-112.840785 33.435962, -112.840785
33.708284, -112.409597 33.708284, -112.409597 33.435962, -112.840785
33.435962)), POLYGON ((-112.309264 33.398167, -112.309264 33.746007,
-111.787444 33.746007, -111.787444 33.398167, -112.309264 33.398167)))",
+ 4326);
+ labelPoint = Functions.asEWKT(Functions.labelPoint(geom, 32));
+ expected = "SRID=4326;POINT (-112.04835399999999 33.57208699999999)";
+ assertEquals(expected, labelPoint);
+
+ geom =
+ Constructors.geomFromWKT(
+ "POLYGON ((-112.654072 33.114485, -112.313516 33.653431,
-111.63515 33.314399, -111.497829 33.874913, -111.692825 33.431378, -112.376684
33.788215, -112.654072 33.114485))",
+ 4326);
+ labelPoint = Functions.asEWKT(Functions.labelPoint(geom, 32, 0.01));
+ expected = "SRID=4326;POINT (-112.0722602222832 33.53914975012836)";
+ assertEquals(expected, labelPoint);
+
+ geom =
+ Constructors.geomFromWKT(
+ "GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167))), POLYGON ((-113.001222 33.223156, -112.991385 33.565242,
-112.650316 33.452315, -113.001222 33.223156)))",
+ 4326);
+ labelPoint = Functions.asEWKT(Functions.labelPoint(geom));
+ expected = "SRID=4326;POINT (-112.04835399999999 33.57208699999999)";
+ assertEquals(expected, labelPoint);
+ }
+
@Test
public void asEWKT() throws Exception {
GeometryFactory geometryFactory = new GeometryFactory(new
PrecisionModel(), 4236);
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index ab5a3cdda5..d735fe97b1 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -183,6 +183,80 @@ Input: `POLYGON ((1 0 1, 1 1 1, 2 2 2, 1 0 1))`
Output: `POLYGON Z((2 3 1, 4 5 1, 7 8 2, 2 3 1))`
+## ST_LabelPoint
+
+Introduction: `ST_LabelPoint` computes and returns a label point for a given
polygon or geometry collection. The label point is chosen to be sufficiently
far from boundaries of the geometry. For a regular Polygon this will be the
+centroid.
+
+The algorithm is derived from Tippecanoe’s `polygon_to_anchor`, an approximate
solution for label point generation, designed to be faster than optimal
algorithms like `polylabel`. It searches for a “good enough” label point within
a limited number of iterations. For geometry collections, only the largest
Polygon by area is considered. While `ST_Centroid` is a fast algorithm to
calculate the center of mass of a (Multi)Polygon, it may place the point
outside of the Polygon or near a bounda [...]
+
+`ST_LabelPoint` takes up to 3 arguments,
+
+- `geometry`: input geometry (e.g., a Polygon or GeometryCollection) for which
the anchor point is to be calculated.
+- `gridResolution` (Optional, default is 16): Controls the resolution of the
search grid for refining the label point. A higher resolution increases the
grid density, providing a higher chance of finding a good enough result at the
cost of runtime. For example, a gridResolution of 16 divides the bounding box
of the polygon into a 16x16 grid.
+- `goodnessThreshold` (Optional, default is 0.2): Determines the minimum
acceptable “goodness” value for the anchor point. Higher thresholds prioritize
points farther from boundaries but may require more computation.
+
+!!!note
+ - `ST_LabelPoint` throws an `IllegalArgumentException` if the input
geometry has an area of zero or less.
+ - Holes within polygons are respected. Points within a hole are given a
goodness of 0.
+ - For GeometryCollections, only the largest polygon by area is considered.
+
+!!!tip
+ - Use `ST_LabelPoint` for tasks such as label placement, identifying
representative points for polygons, or other spatial analyses where an internal
reference point is preferred but not required. If intersection of the point and
the original geometry is required, use of an algorithm like `polylabel` should
be considered.
+ - `ST_LabelPoint` offers a faster, approximate solution for label point
generation, making it ideal for large datasets or real-time applications.
+
+Format:
+
+```sql
+ST_LabelPoint(geometry: Geometry)
+```
+
+```sql
+ST_LabelPoint(geometry: Geometry, gridResolution: Integer)
+```
+
+```sql
+ST_LabelPoint(geometry: Geometry, gridResolution: Integer, goodnessThreshold:
Double)
+```
+
+Since: `v1.7.1`
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))'))
+```
+
+Output:
+
+```
+POINT (2 2)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167)))'))
+```
+
+Output:
+
+```
+POINT (-112.04835399999999 33.57208699999999)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('POLYGON ((-112.654072 33.114485,
-112.313516 33.653431, -111.63515 33.314399, -111.497829 33.874913, -111.692825
33.431378, -112.376684 33.788215, -112.654072 33.114485))', 4326))
+```
+
+Output:
+
+```
+SRID=4326;POINT (-112.0722602222832 33.53914975012836)
+```
+
## ST_Angle
Introduction: Compute and return the angle between two vectors represented by
the provided points or linestrings.
diff --git a/docs/api/snowflake/vector-data/Function.md
b/docs/api/snowflake/vector-data/Function.md
index bb32974120..3fdd484283 100644
--- a/docs/api/snowflake/vector-data/Function.md
+++ b/docs/api/snowflake/vector-data/Function.md
@@ -146,6 +146,78 @@ Input: `POLYGON ((1 0 1, 1 1 1, 2 2 2, 1 0 1))`
Output: `POLYGON Z((2 3 1, 4 5 1, 7 8 2, 2 3 1))`
+## ST_LabelPoint
+
+Introduction: `ST_LabelPoint` computes and returns a label point for a given
polygon or geometry collection. The label point is chosen to be sufficiently
far from boundaries of the geometry. For a regular Polygon this will be the
+centroid.
+
+The algorithm is derived from Tippecanoe’s `polygon_to_anchor`, an approximate
solution for label point generation, designed to be faster than optimal
algorithms like `polylabel`. It searches for a “good enough” label point within
a limited number of iterations. For geometry collections, only the largest
Polygon by area is considered. While `ST_Centroid` is a fast algorithm to
calculate the center of mass of a (Multi)Polygon, it may place the point
outside of the Polygon or near a bounda [...]
+
+`ST_LabelPoint` takes up to 3 arguments,
+
+- `geometry`: input geometry (e.g., a Polygon or GeometryCollection) for which
the anchor point is to be calculated.
+- `gridResolution` (Optional, default is 16): Controls the resolution of the
search grid for refining the label point. A higher resolution increases the
grid density, providing a higher chance of finding a good enough result at the
cost of runtime. For example, a gridResolution of 16 divides the bounding box
of the polygon into a 16x16 grid.
+- `goodnessThreshold` (Optional, default is 0.2): Determines the minimum
acceptable “goodness” value for the anchor point. Higher thresholds prioritize
points farther from boundaries but may require more computation.
+
+!!!note
+ - `ST_LabelPoint` throws an `IllegalArgumentException` if the input
geometry has an area of zero or less.
+ - Holes within polygons are respected. Points within a hole are given a
goodness of 0.
+ - For GeometryCollections, only the largest polygon by area is considered.
+
+!!!tip
+ - Use `ST_LabelPoint` for tasks such as label placement, identifying
representative points for polygons, or other spatial analyses where an internal
reference point is preferred but not required. If intersection of the point and
the original geometry is required, use of an algorithm like `polylabel` should
be considered.
+ - `ST_LabelPoint` offers a faster, approximate solution for label point
generation, making it ideal for large datasets or real-time applications.
+
+Format:
+
+```sql
+ST_LabelPoint(geometry: Geometry)
+```
+
+```sql
+ST_LabelPoint(geometry: Geometry, gridResolution: Integer)
+```
+
+```sql
+ST_LabelPoint(geometry: Geometry, gridResolution: Integer, goodnessThreshold:
Double)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))'))
+```
+
+Output:
+
+```
+POINT (2 2)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167)))'))
+```
+
+Output:
+
+```
+POINT (-112.04835399999999 33.57208699999999)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('POLYGON ((-112.654072 33.114485,
-112.313516 33.653431, -111.63515 33.314399, -111.497829 33.874913, -111.692825
33.431378, -112.376684 33.788215, -112.654072 33.114485))', 4326))
+```
+
+Output:
+
+```
+SRID=4326;POINT (-112.0722602222832 33.53914975012836)
+```
+
## ST_Angle
Introduction: Computes and returns the angle between two vectors represented
by the provided points or linestrings.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index b9bc49c549..f1708d927d 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -179,6 +179,80 @@ Input: `POLYGON ((1 0 1, 1 1 1, 2 2 2, 1 0 1))`
Output: `POLYGON Z((2 3 1, 4 5 1, 7 8 2, 2 3 1))`
+## ST_LabelPoint
+
+Introduction: `ST_LabelPoint` computes and returns a label point for a given
polygon or geometry collection. The label point is chosen to be sufficiently
far from boundaries of the geometry. For a regular Polygon this will be the
+centroid.
+
+The algorithm is derived from Tippecanoe’s `polygon_to_anchor`, an approximate
solution for label point generation, designed to be faster than optimal
algorithms like `polylabel`. It searches for a “good enough” label point within
a limited number of iterations. For geometry collections, only the largest
Polygon by area is considered. While `ST_Centroid` is a fast algorithm to
calculate the center of mass of a (Multi)Polygon, it may place the point
outside of the Polygon or near a bounda [...]
+
+`ST_LabelPoint` takes up to 3 arguments,
+
+- `geometry`: input geometry (e.g., a Polygon or GeometryCollection) for which
the anchor point is to be calculated.
+- `gridResolution` (Optional, default is 16): Controls the resolution of the
search grid for refining the label point. A higher resolution increases the
grid density, providing a higher chance of finding a good enough result at the
cost of runtime. For example, a gridResolution of 16 divides the bounding box
of the polygon into a 16x16 grid.
+- `goodnessThreshold` (Optional, default is 0.2): Determines the minimum
acceptable “goodness” value for the anchor point. Higher thresholds prioritize
points farther from boundaries but may require more computation.
+
+!!!note
+ - `ST_LabelPoint` throws an `IllegalArgumentException` if the input
geometry has an area of zero or less.
+ - Holes within polygons are respected. Points within a hole are given a
goodness of 0.
+ - For GeometryCollections, only the largest polygon by area is considered.
+
+!!!tip
+ - Use `ST_LabelPoint` for tasks such as label placement, identifying
representative points for polygons, or other spatial analyses where an internal
reference point is preferred but not required. If intersection of the point and
the original geometry is required, use of an algorithm like `polylabel` should
be considered.
+ - `ST_LabelPoint` offers a faster, approximate solution for label point
generation, making it ideal for large datasets or real-time applications.
+
+Format:
+
+```sql
+ST_LabelPoint(geometry: Geometry)
+```
+
+```sql
+ST_LabelPoint(geometry: Geometry, gridResolution: Integer)
+```
+
+```sql
+ST_LabelPoint(geometry: Geometry, gridResolution: Integer, goodnessThreshold:
Double)
+```
+
+Since: `v1.7.1`
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('POLYGON((0 0, 4 0, 4 4, 0 4, 0 0))'))
+```
+
+Output:
+
+```
+POINT (2 2)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167)))'))
+```
+
+Output:
+
+```
+POINT (-112.04835399999999 33.57208699999999)
+```
+
+SQL Example:
+
+```
+SELECT ST_LabelPoint(ST_GeomFromWKT('POLYGON ((-112.654072 33.114485,
-112.313516 33.653431, -111.63515 33.314399, -111.497829 33.874913, -111.692825
33.431378, -112.376684 33.788215, -112.654072 33.114485))', 4326))
+```
+
+Output:
+
+```
+SRID=4326;POINT (-112.0722602222832 33.53914975012836)
+```
+
## ST_Angle
Introduction: Computes and returns the angle between two vectors represented
by the provided points or linestrings.
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 396ad16cf0..ba42f9febe 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -90,6 +90,7 @@ public class Catalog {
new Functions.ST_GeometryType(),
new Functions.ST_InterpolatePoint(),
new Functions.ST_Intersection(),
+ new Functions.ST_LabelPoint(),
new Functions.ST_Length(),
new Functions.ST_Length2D(),
new Functions.ST_LengthSpheroid(),
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 15789ad25a..96a00f7b4d 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
@@ -40,6 +40,33 @@ public class Functions {
}
}
+ public static class ST_LabelPoint extends ScalarFunction {
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
+ public Geometry eval(
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
+ Object o) {
+ Geometry geom = (Geometry) o;
+ return org.apache.sedona.common.Functions.labelPoint(geom);
+ }
+
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
+ public Geometry eval(
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o,
+ @DataTypeHint("Integer") Integer gridResolution) {
+ Geometry geom = (Geometry) o;
+ return org.apache.sedona.common.Functions.labelPoint(geom,
gridResolution);
+ }
+
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
+ public Geometry eval(
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o,
+ @DataTypeHint("Integer") Integer gridResolution,
+ @DataTypeHint("Double") Double goodnessThreshold) {
+ Geometry geom = (Geometry) o;
+ return org.apache.sedona.common.Functions.labelPoint(geom,
gridResolution, goodnessThreshold);
+ }
+ }
+
public static class ST_Area extends ScalarFunction {
@DataTypeHint("Double")
public Double eval(
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 ee1f5a9194..dc510c1935 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -48,6 +48,36 @@ public class FunctionTest extends TestBase {
initialize();
}
+ @Test
+ public void testAnchor() {
+ String actual =
+ (String)
+ first(
+ tableEnv.sqlQuery(
+ "SELECT
ST_AsText(ST_ReducePrecision(ST_LabelPoint(ST_GeomFromWKT('POLYGON
((-112.637484 33.440546, -112.546852 33.477209, -112.489177 33.550488,
-112.41777 33.751684, -111.956371 33.719707, -111.766868 33.616843, -111.775107
33.527595, -111.640533 33.504695, -111.440044 33.463462, -111.415326 33.374055,
-111.514197 33.309809, -111.643279 33.222542, -111.893203 33.174278, -111.96461
33.250109, -112.123903 33.261593, -112.252985 33.35341, -112.406784 33.346527,
-11 [...]
+ .getField(0);
+ String expected = "POINT (-112.0428 33.4642)";
+ assertEquals(expected, actual);
+
+ actual =
+ (String)
+ first(
+ tableEnv.sqlQuery(
+ "SELECT
ST_AsText(ST_ReducePrecision(ST_LabelPoint(ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON
((-112.840785 33.435962, -112.840785 33.708284, -112.409597 33.708284,
-112.409597 33.435962, -112.840785 33.435962)), POLYGON ((-112.309264
33.398167, -112.309264 33.746007, -111.787444 33.746007, -111.787444 33.398167,
-112.309264 33.398167)))'), 4), 4))"))
+ .getField(0);
+ expected = "POINT (-112.0484 33.5721)";
+ assertEquals(expected, actual);
+
+ actual =
+ (String)
+ first(
+ tableEnv.sqlQuery(
+ "SELECT
ST_AsText(ST_ReducePrecision(ST_LabelPoint(ST_GeomFromWKT('POLYGON
((-112.654072 33.114485, -112.313516 33.653431, -111.63515 33.314399,
-111.497829 33.874913, -111.692825 33.431378, -112.376684 33.788215,
-112.654072 33.114485))')), 4))"))
+ .getField(0);
+ expected = "POINT (-112.0723 33.5391)";
+ assertEquals(expected, actual);
+ }
+
@Test
public void testArea() {
Table polygonTable = createPolygonTable(1);
diff --git a/python/sedona/sql/st_functions.py
b/python/sedona/sql/st_functions.py
index 9e77909c15..f4924c8de0 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -100,6 +100,36 @@ def ST_AddPoint(
return _call_st_function("ST_AddPoint", args)
+@validate_argument_types
+def ST_LabelPoint(
+ geometry: ColumnOrName,
+ gridResolution: Optional[Union[ColumnOrNameOrNumber, int]] = None,
+ goodnessThreshold: Optional[Union[ColumnOrNameOrNumber, float]] = None,
+) -> Column:
+ """Calculate an anchor point for a given geometry column.
+
+ :param geometry: Input geometry column to calculate the anchor for.
+ :type geometry: ColumnOrName
+ :param gridResolution: Optional step size for grid search when determining
the best anchor point.
+ Defaults to 2 if not provided.
+ :type gridResolution: Optional[Union[ColumnOrNameOrNumber, int]], optional
+ :param goodnessThreshold: Optional threshold for the minimum "goodness"
value.
+ Determines when to stop refining the anchor
search.
+ Defaults to 0.2 if not provided.
+ :type goodnessThreshold: Optional[Union[ColumnOrNameOrNumber, float]],
optional
+ :return: Anchor point as a geometry column.
+ :rtype: Column
+ """
+ if gridResolution is None and goodnessThreshold is None:
+ args = (geometry,)
+ elif goodnessThreshold is None:
+ args = (geometry, gridResolution)
+ else:
+ args = (geometry, gridResolution, goodnessThreshold)
+
+ return _call_st_function("ST_LabelPoint", args)
+
+
@validate_argument_types
def ST_Area(geometry: ColumnOrName) -> Column:
"""Calculate the area of a geometry.
diff --git a/python/tests/sql/test_dataframe_api.py
b/python/tests/sql/test_dataframe_api.py
index 5683d6d29c..f839b762bb 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -267,6 +267,27 @@ test_configurations = [
"",
"LINESTRING (0 0, 1 1, 1 0, 2 0, 3 0, 4 0, 5 0)",
),
+ (
+ stf.ST_LabelPoint,
+ ("geom",),
+ "triangle_geom",
+ "",
+ "POINT (0.6666666666666666 0.3333333333333333)",
+ ),
+ (
+ stf.ST_LabelPoint,
+ ("geom", 1),
+ "triangle_geom",
+ "",
+ "POINT (0.6666666666666666 0.3333333333333333)",
+ ),
+ (
+ stf.ST_LabelPoint,
+ ("geom", 1, 0.5),
+ "triangle_geom",
+ "",
+ "POINT (0.6666666666666666 0.3333333333333333)",
+ ),
(
stf.ST_Angle,
(
@@ -1287,6 +1308,7 @@ wrong_type_configurations = [
(stf.ST_IsValidDetail, (None,)),
(stf.ST_IsValid, (None,)),
(stf.ST_IsValidReason, (None,)),
+ (stf.ST_LabelPoint, (None,)),
(stf.ST_Length, (None,)),
(stf.ST_Length2D, (None,)),
(stf.ST_LineFromMultiPoint, (None,)),
diff --git a/python/tests/sql/test_function.py
b/python/tests/sql/test_function.py
index 6ea0d944bd..6c1e968c0d 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -54,6 +54,25 @@ class TestPredicateJoin(TestBase):
]
)
+ def test_ST_LabelPoint(self):
+ geom_expr = "ST_GeomFromWKT('POLYGON ((-112.637484 33.440546,
-112.546852 33.477209, -112.489177 33.550488, -112.41777 33.751684, -111.956371
33.719707, -111.766868 33.616843, -111.775107 33.527595, -111.640533 33.504695,
-111.440044 33.463462, -111.415326 33.374055, -111.514197 33.309809,
-111.643279 33.222542, -111.893203 33.174278, -111.96461 33.250109, -112.123903
33.261593, -112.252985 33.35341, -112.406784 33.346527, -112.667694 33.316695,
-112.637484 33.440546))')"
+ function_df = self.spark.sql(f"select ST_LabelPoint({geom_expr})")
+ actual = function_df.take(1)[0][0]
+ expected = "POINT (-112.04278737349767 33.46420809489905)"
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
+
+ geom_expr = "ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167)))')"
+ function_df = self.spark.sql(f"select ST_LabelPoint({geom_expr}, 1)")
+ actual = function_df.take(1)[0][0]
+ expected = "POINT (-112.04835399999999 33.57208699999999)"
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
+
+ geom_expr = "ST_GeomFromWKT('POLYGON ((-112.654072 33.114485,
-112.313516 33.653431, -111.63515 33.314399, -111.497829 33.874913, -111.692825
33.431378, -112.376684 33.788215, -112.654072 33.114485))')"
+ function_df = self.spark.sql(f"select ST_LabelPoint({geom_expr}, 1,
0.1)")
+ actual = function_df.take(1)[0][0]
+ expected = "POINT (-112.0722602222832 33.53914975012836)"
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
+
def test_st_concave_hull(self):
polygon_wkt_df = (
self.spark.read.format("csv")
diff --git
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
index ed319f92bc..916c4e3371 100644
---
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
+++
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
@@ -81,6 +81,25 @@ public class TestFunctions extends TestBase {
"POINT (2 2)");
}
+ @Test
+ public void test_ST_LabelPoint() {
+ registerUDF("ST_LabelPoint", byte[].class, int.class, double.class);
+ registerUDF("ST_ReducePrecision", byte[].class, int.class);
+ verifySqlSingleRes(
+ "SELECT
sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_LabelPoint(sedona.ST_GeomFromText('POLYGON
((-112.637484 33.440546, -112.546852 33.477209, -112.489177 33.550488,
-112.41777 33.751684, -111.956371 33.719707, -111.766868 33.616843, -111.775107
33.527595, -111.640533 33.504695, -111.440044 33.463462, -111.415326 33.374055,
-111.514197 33.309809, -111.643279 33.222542, -111.893203 33.174278, -111.96461
33.250109, -112.123903 33.261593, -112.252985 33.35341, -112.406784 3 [...]
+ "POINT (-112.0428 33.4642)");
+ registerUDF("ST_LabelPoint", byte[].class, int.class);
+ registerUDF("ST_ReducePrecision", byte[].class, int.class);
+ verifySqlSingleRes(
+ "SELECT
sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_LabelPoint(sedona.ST_GeomFromText('GEOMETRYCOLLECTION(POLYGON
((-112.840785 33.435962, -112.840785 33.708284, -112.409597 33.708284,
-112.409597 33.435962, -112.840785 33.435962)), POLYGON ((-112.309264
33.398167, -112.309264 33.746007, -111.787444 33.746007, -111.787444 33.398167,
-112.309264 33.398167)))'), 4), 4))",
+ "POINT (-112.0484 33.5721)");
+ registerUDF("ST_LabelPoint", byte[].class);
+ registerUDF("ST_ReducePrecision", byte[].class, int.class);
+ verifySqlSingleRes(
+ "SELECT
sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_LabelPoint(sedona.ST_GeomFromText('POLYGON
((-112.654072 33.114485, -112.313516 33.653431, -111.63515 33.314399,
-111.497829 33.874913, -111.692825 33.431378, -112.376684 33.788215,
-112.654072 33.114485))')), 4))",
+ "POINT (-112.0723 33.5391)");
+ }
+
@Test
public void test_ST_Angle() {
registerUDF("ST_Angle", byte[].class, byte[].class);
diff --git
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
index cfcdddcd20..82649398ff 100644
---
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
+++
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
@@ -80,6 +80,22 @@ public class TestFunctionsV2 extends TestBase {
"POINT(2 2)");
}
+ @Test
+ public void test_ST_LabelPoint() {
+ registerUDFV2("ST_LabelPoint", String.class, int.class, double.class);
+ verifyGeometry(
+ "SELECT
ST_AsText(ST_ReducePrecision(sedona.ST_LabelPoint(ST_GeometryFromWKT('POLYGON
((-112.637484 33.440546, -112.546852 33.477209, -112.489177 33.550488,
-112.41777 33.751684, -111.956371 33.719707, -111.766868 33.616843, -111.775107
33.527595, -111.640533 33.504695, -111.440044 33.463462, -111.415326 33.374055,
-111.514197 33.309809, -111.643279 33.222542, -111.893203 33.174278, -111.96461
33.250109, -112.123903 33.261593, -112.252985 33.35341, -112.406784 33.346527,
-112.667 [...]
+ "POINT (-112.0428 33.4642)");
+ registerUDFV2("ST_LabelPoint", String.class, int.class);
+ verifyGeometry(
+ "SELECT
ST_AsText(ST_ReducePrecision(sedona.ST_LabelPoint(ST_GeometryFromWKT('GEOMETRYCOLLECTION(POLYGON
((-112.840785 33.435962, -112.840785 33.708284, -112.409597 33.708284,
-112.409597 33.435962, -112.840785 33.435962)), POLYGON ((-112.309264
33.398167, -112.309264 33.746007, -111.787444 33.746007, -111.787444 33.398167,
-112.309264 33.398167)))'), 4), 4))",
+ "POINT (-112.0484 33.5721)");
+ registerUDFV2("ST_LabelPoint", String.class);
+ verifyGeometry(
+ "SELECT
ST_AsText(ST_ReducePrecision(sedona.ST_LabelPoint(ST_GeometryFromWKT('POLYGON
((-112.654072 33.114485, -112.313516 33.653431, -111.63515 33.314399,
-111.497829 33.874913, -111.692825 33.431378, -112.376684 33.788215,
-112.654072 33.114485))')), 4))",
+ "POINT (-112.0723 33.5391)");
+ }
+
@Test
public void test_ST_Angle() {
registerUDFV2("ST_Angle", String.class, String.class);
diff --git
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
index 368cdb3e4b..485e54730b 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
@@ -87,6 +87,23 @@ public class UDFs {
return Functions.angle(GeometrySerde.deserialize(geom1),
GeometrySerde.deserialize(geom2));
}
+ @UDFAnnotations.ParamMeta(argNames = {"geom"})
+ public static byte[] ST_LabelPoint(byte[] geom) {
+ return
GeometrySerde.serialize(Functions.labelPoint(GeometrySerde.deserialize(geom)));
+ }
+
+ @UDFAnnotations.ParamMeta(argNames = {"geom", "gridResolution"})
+ public static byte[] ST_LabelPoint(byte[] geom, int gridResolution) {
+ return GeometrySerde.serialize(
+ Functions.labelPoint(GeometrySerde.deserialize(geom), gridResolution));
+ }
+
+ @UDFAnnotations.ParamMeta(argNames = {"geom", "gridResolution",
"goodnessThreshold"})
+ public static byte[] ST_LabelPoint(byte[] geom, int gridResolution, double
goodnessThreshold) {
+ return GeometrySerde.serialize(
+ Functions.labelPoint(GeometrySerde.deserialize(geom), gridResolution,
goodnessThreshold));
+ }
+
@UDFAnnotations.ParamMeta(argNames = {"geom1", "geom2", "geom3"})
public static double ST_Angle(byte[] geom1, byte[] geom2, byte[] geom3) {
return Functions.angle(
diff --git
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
index a8e6e2efd6..c7aadd935f 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
@@ -101,6 +101,29 @@ public class UDFsV2 {
Functions.affine(GeometrySerde.deserGeoJson(geometry), a, b, d, e,
xOff, yOff));
}
+ @UDFAnnotations.ParamMeta(
+ argNames = {"geom"},
+ argTypes = {"Geometry"})
+ public static String ST_LabelPoint(String geom) {
+ return
GeometrySerde.serGeoJson(Functions.labelPoint(GeometrySerde.deserGeoJson(geom)));
+ }
+
+ @UDFAnnotations.ParamMeta(
+ argNames = {"geom", "gridResolution"},
+ argTypes = {"Geometry", "int"})
+ public static String ST_LabelPoint(String geom, int gridResolution) {
+ return GeometrySerde.serGeoJson(
+ Functions.labelPoint(GeometrySerde.deserGeoJson(geom),
gridResolution));
+ }
+
+ @UDFAnnotations.ParamMeta(
+ argNames = {"geom", "gridResolution", "goodnessThreshold"},
+ argTypes = {"Geometry", "int", "double"})
+ public static String ST_LabelPoint(String geom, int gridResolution, double
goodnessThreshold) {
+ return GeometrySerde.serGeoJson(
+ Functions.labelPoint(GeometrySerde.deserGeoJson(geom), gridResolution,
goodnessThreshold));
+ }
+
@UDFAnnotations.ParamMeta(
argNames = {"geom1", "geom2"},
argTypes = {"Geometry", "Geometry"})
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 b491375379..b616ecd790 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
@@ -38,6 +38,7 @@ object Catalog {
val expressions: Seq[FunctionDescription] = Seq(
// Expression for vectors
function[GeometryType](),
+ function[ST_LabelPoint](),
function[ST_PointFromText](),
function[ST_PointFromWKB](),
function[ST_LineFromWKB](),
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 dc8a290b8a..0f2a20713f 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
@@ -35,6 +35,17 @@ import
org.apache.spark.sql.sedona_sql.expressions.InferrableFunctionConverter._
import org.apache.spark.unsafe.types.UTF8String
import org.apache.spark.util.Utils
+case class ST_LabelPoint(inputExpressions: Seq[Expression])
+ extends InferredExpression(
+ inferrableFunction1(Functions.labelPoint),
+ inferrableFunction2(Functions.labelPoint),
+ inferrableFunction3(Functions.labelPoint)) {
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
/**
* Return the distance between two geometries.
*
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 7bb753cc28..0defb89a2f 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
@@ -45,6 +45,22 @@ object st_functions extends DataFrameAPI {
def ST_AddPoint(lineString: String, point: String, index: Int): Column =
wrapExpression[ST_AddPoint](lineString, point, index)
+ def ST_LabelPoint(geometry: Column): Column =
+ wrapExpression[ST_LabelPoint](geometry)
+ def ST_LabelPoint(geometry: String): Column =
+ wrapExpression[ST_LabelPoint](geometry)
+ def ST_LabelPoint(geometry: Column, gridResolution: Column): Column =
+ wrapExpression[ST_LabelPoint](geometry, gridResolution)
+ def ST_LabelPoint(geometry: String, gridResolution: Integer): Column =
+ wrapExpression[ST_LabelPoint](geometry, gridResolution)
+ def ST_LabelPoint(geometry: Column, gridResolution: Column,
goodnessThreshold: Column): Column =
+ wrapExpression[ST_LabelPoint](geometry, gridResolution, goodnessThreshold)
+ def ST_LabelPoint(
+ geometry: String,
+ gridResolution: Integer,
+ goodnessThreshold: Double): Column =
+ wrapExpression[ST_LabelPoint](geometry, gridResolution, goodnessThreshold)
+
def ST_Area(geometry: Column): Column = wrapExpression[ST_Area](geometry)
def ST_Area(geometry: String): Column = wrapExpression[ST_Area](geometry)
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
index ea6092629f..78c2227642 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
@@ -54,6 +54,7 @@ class PreserveSRIDSuite extends TestBaseScala with
TableDrivenPropertyChecks {
("ST_SimplifyVW(geom1, 0.1)", 1000),
("ST_SimplifyPolygonHull(geom1, 0.5)", 1000),
("ST_SetSRID(geom1, 2000)", 2000),
+ ("ST_LabelPoint(geom1)", 1000),
("ST_LineMerge(geom2)", 1000),
("ST_StartPoint(geom3)", 1000),
("ST_Snap(geom3, geom3, 0.1)", 1000),
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 cf9b8a0f7a..f1ece09481 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
@@ -411,6 +411,38 @@ class dataFrameAPITestScala extends TestBaseScala {
}
// functions
+ it("Passed ST_LabelPoint") {
+ var geomDf = sparkSession.sql(
+ "SELECT ST_GeomFromWKT('POLYGON ((-112.637484 33.440546, -112.546852
33.477209, -112.489177 33.550488, -112.41777 33.751684, -111.956371 33.719707,
-111.766868 33.616843, -111.775107 33.527595, -111.640533 33.504695,
-111.440044 33.463462, -111.415326 33.374055, -111.514197 33.309809,
-111.643279 33.222542, -111.893203 33.174278, -111.96461 33.250109, -112.123903
33.261593, -112.252985 33.35341, -112.406784 33.346527, -112.667694 33.316695,
-112.637484 33.440546))') AS geom, 2.0 [...]
+ var result = geomDf.select(
+ ST_LabelPoint(col("geom"), col("gridResolution"),
col("goodnessThreshold")).as("geom"))
+ var actualResult =
result.take(1)(0).get(0).asInstanceOf[Geometry].toText()
+ var expected = "POINT (-112.04278737349767 33.46420809489905)"
+ assertEquals(expected, actualResult)
+
+ geomDf = sparkSession.sql(
+ "SELECT ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167)))') AS geom")
+ geomDf.createOrReplaceTempView("geomDf")
+ result = geomDf.select(ST_LabelPoint(col("geom"), lit(1)).as("geom"))
+ actualResult = result.take(1)(0).get(0).asInstanceOf[Geometry].toText()
+ expected = "POINT (-112.04835399999999 33.57208699999999)"
+ assertEquals(expected, actualResult)
+
+ geomDf = sparkSession.sql(
+ "SELECT ST_GeomFromWKT('POLYGON ((-112.654072 33.114485, -112.313516
33.653431, -111.63515 33.314399, -111.497829 33.874913, -111.692825 33.431378,
-112.376684 33.788215, -112.654072 33.114485))') AS geom, 0.01 AS
goodnessThreshold")
+ geomDf.createOrReplaceTempView("geomDf")
+ result =
+ geomDf.select(ST_LabelPoint(col("geom"), lit(2),
col("goodnessThreshold")).as("geom"))
+ actualResult = result.take(1)(0).get(0).asInstanceOf[Geometry].toText()
+ expected = "POINT (-112.0722602222832 33.53914975012836)"
+ assertEquals(expected, actualResult)
+
+ result = geomDf.select(ST_LabelPoint(col("geom")).as("geom"))
+ actualResult = result.take(1)(0).get(0).asInstanceOf[Geometry].toText()
+ expected = "POINT (-112.0722602222832 33.53914975012836)"
+ assertEquals(expected, actualResult)
+ }
+
it("Passed ST_ConcaveHull") {
val baseDF = sparkSession.sql(
"SELECT ST_GeomFromWKT('Polygon ((0 0, 1 2, 2 2, 3 2, 5 0, 4 0, 3 1, 2
1, 1 0, 0 0))') as mline")
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index 82dbff82c1..90848ebf83 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -49,6 +49,36 @@ class functionTestScala
describe("Sedona-SQL Function Test") {
+ it("Passed ST_LabelPoint") {
+ var geomDf = sparkSession.sql(
+ "SELECT ST_GeomFromWKT('POLYGON ((-112.637484 33.440546, -112.546852
33.477209, -112.489177 33.550488, -112.41777 33.751684, -111.956371 33.719707,
-111.766868 33.616843, -111.775107 33.527595, -111.640533 33.504695,
-111.440044 33.463462, -111.415326 33.374055, -111.514197 33.309809,
-111.643279 33.222542, -111.893203 33.174278, -111.96461 33.250109, -112.123903
33.261593, -112.252985 33.35341, -112.406784 33.346527, -112.667694 33.316695,
-112.637484 33.440546))') AS geom, 2 AS [...]
+ geomDf.createOrReplaceTempView("geomDf")
+ var result =
+ sparkSession.sql(
+ "SELECT ST_AsEWKT(ST_LabelPoint(geom, gridResolution,
goodnessThreshold)) FROM geomDf")
+ var expected = "POINT (-112.04278737349767 33.46420809489905)"
+ assertEquals(expected, result.take(1)(0).get(0).asInstanceOf[String])
+
+ geomDf = sparkSession.sql(
+ "SELECT ST_GeomFromWKT('GEOMETRYCOLLECTION(POLYGON ((-112.840785
33.435962, -112.840785 33.708284, -112.409597 33.708284, -112.409597 33.435962,
-112.840785 33.435962)), POLYGON ((-112.309264 33.398167, -112.309264
33.746007, -111.787444 33.746007, -111.787444 33.398167, -112.309264
33.398167)))') AS geom")
+ geomDf.createOrReplaceTempView("geomDf")
+ result = sparkSession.sql("SELECT ST_AsEWKT(ST_LabelPoint(geom, 1)) FROM
geomDf")
+ expected = "POINT (-112.04835399999999 33.57208699999999)"
+ assertEquals(expected, result.take(1)(0).get(0).asInstanceOf[String])
+
+ geomDf = sparkSession.sql(
+ "SELECT ST_GeomFromWKT('POLYGON ((-112.654072 33.114485, -112.313516
33.653431, -111.63515 33.314399, -111.497829 33.874913, -111.692825 33.431378,
-112.376684 33.788215, -112.654072 33.114485))') AS geom, 0.01 AS
goodnessThreshold")
+ geomDf.createOrReplaceTempView("geomDf")
+ result = sparkSession.sql(
+ "SELECT ST_AsEWKT(ST_LabelPoint(geom, 2, goodnessThreshold)) FROM
geomDf")
+ expected = "POINT (-112.0722602222832 33.53914975012836)"
+ assertEquals(expected, result.take(1)(0).get(0).asInstanceOf[String])
+
+ result = sparkSession.sql("SELECT ST_AsEWKT(ST_LabelPoint(geom)) FROM
geomDf")
+ expected = "POINT (-112.0722602222832 33.53914975012836)"
+ assertEquals(expected, result.take(1)(0).get(0).asInstanceOf[String])
+ }
+
it("Passed ST_ConcaveHull") {
var polygonWktDf = sparkSession.read
.format("csv")