Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public static int nPoints(Geography g) {
return toJTS(g).getNumPoints();
}

/** Return the WKT text representation of a geography. */
public static String asText(Geography g) {
if (g == null) return null;
return toJTS(g).toText();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhangfengcdt This toJTS function seem to SerDe WKB one more time. Is it possible to avoid it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! toJTS(g) on WKBGeography does one WKB → JTS parse and caches the result in jtsGeometry (WKBGeography.java:116). Subsequent calls — both later asText invocations on the same row and any other JTS-path function (ST_NPoints, ST_GeometryType, ST_Centroid, ST_NumGeometries) — reuse the cached Geometry for free.

The alternative — g.toString() on the Geography — actually goes through WKBGeography.toString() getS2Geography().toString(), which populates the other cache (s2Geography), so it's worse when the current implementation.

A true zero-parse path (WKB → WKT walker) would need custom code since JTS doesn't expose it.

}

// ─── Level 2: JTS + S2 geodesic metrics ──────────────────────────────────

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@
import org.apache.sedona.common.geography.Constructors;
import org.apache.sedona.common.geography.Functions;
import org.junit.Test;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;

public class FunctionTest {
private static final double EPS = 1e-9;
Expand Down Expand Up @@ -144,6 +148,37 @@ public void nPoints_polygon() throws ParseException {
assertEquals(5, Functions.nPoints(g));
}

@Test
public void asText_point() throws ParseException {
Geography g = Constructors.geogFromWKT("POINT (1 2)", 4326);
String wkt = Functions.asText(g);
assertNotNull(wkt);
Point p = (Point) new WKTReader().read(wkt);
// S2 round-trip may introduce sub-nanometer floating-point drift; use a loose tolerance.
assertEquals(1.0, p.getX(), 1e-9);
assertEquals(2.0, p.getY(), 1e-9);
}
Comment thread
zhangfengcdt marked this conversation as resolved.

@Test
public void asText_polygon() throws ParseException {
Geography g = Constructors.geogFromWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", 4326);
String wkt = Functions.asText(g);
assertNotNull(wkt);
Polygon poly = (Polygon) new WKTReader().read(wkt);
Coordinate[] ring = poly.getExteriorRing().getCoordinates();
assertEquals(5, ring.length);
double[][] expected = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
for (int i = 0; i < expected.length; i++) {
assertEquals("ring[" + i + "].x", expected[i][0], ring[i].x, 1e-9);
assertEquals("ring[" + i + "].y", expected[i][1], ring[i].y, 1e-9);
}
}
Comment thread
zhangfengcdt marked this conversation as resolved.

@Test
public void asText_nullHandling() {
assertNull(Functions.asText(null));
}

// ─── Level 2: ST_Distance ────────────────────────────────────────────────

@Test
Expand Down
1 change: 1 addition & 0 deletions docs/api/sql/geography/Geography-Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ These functions operate on geography type objects.
| Function | Return type | Description | Since |
| :--- | :--- | :--- | :--- |
| [ST_AsEWKT](Geography-Functions/ST_AsEWKT.md) | String | Return the Extended Well-Known Text representation of a geography. | v1.8.0 |
| [ST_AsText](Geography-Functions/ST_AsText.md) | String | Return the Well-Known Text (WKT) representation of a geography. | v1.9.1 |
| [ST_Envelope](Geography-Functions/ST_Envelope.md) | Geography | Return the bounding box (envelope) of a geography. Supports anti-meridian splitting. | v1.8.0 |
| [ST_NPoints](Geography-Functions/ST_NPoints.md) | Integer | Return the number of points (vertices) in a geography. | v1.9.0 |
| [ST_Distance](Geography-Functions/ST_Distance.md) | Double | Return the minimum geodesic distance between two geographies in meters. | v1.9.0 |
Expand Down
42 changes: 42 additions & 0 deletions docs/api/sql/geography/Geography-Functions/ST_AsText.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!--
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_AsText

Introduction: Returns the Well-Known Text (WKT) representation of a geography object.

Format:

`ST_AsText (A: Geography)`

Return type: `String`

Since: `v1.9.1`

SQL Example

```sql
SELECT ST_AsText(ST_GeogFromWKT('POINT (1 2)'));
```

Output:

```
POINT (1 2)
```
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,9 @@ private[apache] case class ST_SimplifyPolygonHull(inputExpressions: Seq[Expressi
}

private[apache] case class ST_AsText(inputExpressions: Seq[Expression])
extends InferredExpression(Functions.asWKT _) {
extends InferredExpression(
inferrableFunction1(Functions.asWKT),
inferrableFunction1(org.apache.sedona.common.geography.Functions.asText)) {

protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import org.apache.sedona.sql.TestBaseScala
import org.apache.spark.sql.functions.{col, lit}
import org.apache.spark.sql.sedona_sql.expressions.{st_constructors, st_functions, st_predicates}
import org.junit.Assert.{assertEquals, assertNotNull, assertTrue}
import org.locationtech.jts.geom.Geometry
import org.locationtech.jts.geom.{Geometry, Point}
import org.locationtech.jts.io.WKTReader

/**
* Spark SQL integration tests for Geography ST functions. Tests one representative function per
Expand Down Expand Up @@ -77,7 +78,7 @@ class GeographyFunctionTest extends TestBaseScala {
}
}

// ─── Level 1: ST_NPoints ───────────────────────────────────────────────
// ─── Level 1: ST_NPoints, ST_AsText ────────────────────────────────────

describe("Level 1: Structural") {

Expand All @@ -87,6 +88,17 @@ class GeographyFunctionTest extends TestBaseScala {
.first()
assertEquals(3, row.getInt(0))
}

it("ST_AsText") {
val row = sparkSession
.sql("SELECT ST_AsText(ST_GeogFromWKT('POINT (1 2)', 4326)) AS wkt")
.first()
val wkt = row.getString(0)
val point = new WKTReader().read(wkt).asInstanceOf[Point]
// S2 round-trip may introduce sub-nanometer floating-point drift; use a loose tolerance.
assertEquals(1.0, point.getX, 1e-9)
assertEquals(2.0, point.getY, 1e-9)
}
Comment thread
zhangfengcdt marked this conversation as resolved.
}

// ─── Level 2: ST_Distance ──────────────────────────────────────────────
Expand Down
Loading