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 2022/08/24 18:08:10 UTC

[incubator-sedona] branch master updated: [SEDONA-152] Add reader/writer functions for GML and KML (#673)

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/incubator-sedona.git


The following commit(s) were added to refs/heads/master by this push:
     new 5c7ac9a6 [SEDONA-152] Add reader/writer functions for GML and KML (#673)
5c7ac9a6 is described below

commit 5c7ac9a64774c44a3071588b06101106bbefe395
Author: Kengo Seki <se...@apache.org>
AuthorDate: Thu Aug 25 03:08:04 2022 +0900

    [SEDONA-152] Add reader/writer functions for GML and KML (#673)
---
 .../java/org/apache/sedona/common/Functions.java   | 10 +++++
 docs/api/flink/Constructor.md                      | 28 +++++++++++++
 docs/api/flink/Function.md                         | 28 +++++++++++++
 docs/api/sql/Constructor.md                        | 28 +++++++++++++
 docs/api/sql/Function.md                           | 28 +++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  4 ++
 .../sedona/flink/expressions/Constructors.java     | 21 ++++++++++
 .../apache/sedona/flink/expressions/Functions.java | 16 ++++++++
 .../org/apache/sedona/flink/ConstructorTest.java   | 44 +++++++++++++++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java | 34 ++++++++++++++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  4 ++
 .../sql/sedona_sql/expressions/Constructors.scala  | 46 ++++++++++++++++++++++
 .../sql/sedona_sql/expressions/Functions.scala     | 16 ++++++++
 .../apache/sedona/sql/constructorTestScala.scala   | 30 +++++++++++++-
 .../org/apache/sedona/sql/functionTestScala.scala  | 33 +++++++++++++++-
 15 files changed, 367 insertions(+), 3 deletions(-)

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 1cc97086..f51d7e79 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -28,6 +28,8 @@ import org.locationtech.jts.geom.MultiPoint;
 import org.locationtech.jts.geom.MultiPolygon;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
+import org.locationtech.jts.io.gml2.GMLWriter;
+import org.locationtech.jts.io.kml.KMLWriter;
 import org.locationtech.jts.operation.valid.IsSimpleOp;
 import org.locationtech.jts.operation.valid.IsValidOp;
 import org.opengis.referencing.FactoryException;
@@ -139,6 +141,14 @@ public class Functions {
         return writer.write(geometry).toString();
     }
 
+    public static String asGML(Geometry geometry) {
+        return new GMLWriter().write(geometry);
+    }
+
+    public static String asKML(Geometry geometry) {
+        return new KMLWriter().write(geometry);
+    }
+
     public static Geometry force2D(Geometry geometry) {
         return GeomUtils.get2dGeom(geometry);
     }
diff --git a/docs/api/flink/Constructor.md b/docs/api/flink/Constructor.md
index 9b133a93..2999fef7 100644
--- a/docs/api/flink/Constructor.md
+++ b/docs/api/flink/Constructor.md
@@ -25,6 +25,34 @@ SELECT ST_GeomFromGeoJSON(polygontable._c0) AS polygonshape
 FROM polygontable
 ```
 
+## ST_GeomFromGML
+
+Introduction: Construct a Geometry from GML.
+
+Format:
+`ST_GeomFromGML (gml:string)`
+
+Since: `v1.3.0`
+
+SQL example:
+```SQL
+SELECT ST_GeomFromGML('<gml:LineString srsName="EPSG:4269"><gml:coordinates>-71.16028,42.258729 -71.160837,42.259112 -71.161143,42.25932</gml:coordinates></gml:LineString>') AS geometry
+```
+
+## ST_GeomFromKML
+
+Introduction: Construct a Geometry from KML.
+
+Format:
+`ST_GeomFromKML (kml:string)`
+
+Since: `v1.3.0`
+
+SQL example:
+```SQL
+SELECT ST_GeomFromKML('<LineString><coordinates>-71.1663,42.2614 -71.1667,42.2616</coordinates></LineString>') AS geometry
+```
+
 ## ST_GeomFromText
 
 Introduction: Construct a Geometry from Wkt. Alias of  [ST_GeomFromWKT](#ST_GeomFromWKT)
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 037bbae7..b575448d 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -63,6 +63,34 @@ SELECT ST_AsGeoJSON(polygondf.countyshape)
 FROM polygondf
 ```
 
+## ST_AsGML
+
+Introduction: Return the [GML](https://www.ogc.org/standards/gml) string representation of a geometry
+
+Format: `ST_AsGML (A:geometry)`
+
+Since: `v1.3.0`
+
+Spark SQL example:
+```SQL
+SELECT ST_AsGML(polygondf.countyshape)
+FROM polygondf
+```
+
+## ST_AsKML
+
+Introduction: Return the [KML](https://www.ogc.org/standards/kml) string representation of a geometry
+
+Format: `ST_AsKML (A:geometry)`
+
+Since: `v1.3.0`
+
+Spark SQL example:
+```SQL
+SELECT ST_AsKML(polygondf.countyshape)
+FROM polygondf
+```
+
 ## ST_AsText
 
 Introduction: Return the Well-Known Text string representation of a geometry
diff --git a/docs/api/sql/Constructor.md b/docs/api/sql/Constructor.md
index 55f55ef3..903b4164 100644
--- a/docs/api/sql/Constructor.md
+++ b/docs/api/sql/Constructor.md
@@ -90,6 +90,34 @@ polygonDf.show()
 !!!warning
 	The way that SedonaSQL reads GeoJSON is different from that in SparkSQL
 
+## ST_GeomFromGML
+
+Introduction: Construct a Geometry from GML.
+
+Format:
+`ST_GeomFromGML (gml:string)`
+
+Since: `v1.3.0`
+
+SQL example:
+```SQL
+SELECT ST_GeomFromGML('<gml:LineString srsName="EPSG:4269"><gml:coordinates>-71.16028,42.258729 -71.160837,42.259112 -71.161143,42.25932</gml:coordinates></gml:LineString>') AS geometry
+```
+
+## ST_GeomFromKML
+
+Introduction: Construct a Geometry from KML.
+
+Format:
+`ST_GeomFromKML (kml:string)`
+
+Since: `v1.3.0`
+
+SQL example:
+```SQL
+SELECT ST_GeomFromKML('<LineString><coordinates>-71.1663,42.2614 -71.1667,42.2616</coordinates></LineString>') AS geometry
+```
+
 ## ST_GeomFromText
 
 Introduction: Construct a Geometry from Wkt. Alias of [ST_GeomFromWKT](#ST_GeomFromWKT)
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 127dadc1..f60ca1e2 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -113,6 +113,34 @@ SELECT ST_AsGeoJSON(polygondf.countyshape)
 FROM polygondf
 ```
 
+## ST_AsGML
+
+Introduction: Return the [GML](https://www.ogc.org/standards/gml) string representation of a geometry
+
+Format: `ST_AsGML (A:geometry)`
+
+Since: `v1.3.0`
+
+Spark SQL example:
+```SQL
+SELECT ST_AsGML(polygondf.countyshape)
+FROM polygondf
+```
+
+## ST_AsKML
+
+Introduction: Return the [KML](https://www.ogc.org/standards/kml) string representation of a geometry
+
+Format: `ST_AsKML (A:geometry)`
+
+Since: `v1.3.0`
+
+Spark SQL example:
+```SQL
+SELECT ST_AsKML(polygondf.countyshape)
+FROM polygondf
+```
+
 ## ST_AsText
 
 Introduction: Return the Well-Known Text string representation of a geometry
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 887c8cb9..a2e365c9 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -32,6 +32,8 @@ public class Catalog {
                 new Constructors.ST_GeomFromWKB(),
                 new Constructors.ST_GeomFromGeoJSON(),
                 new Constructors.ST_GeomFromGeoHash(),
+                new Constructors.ST_GeomFromGML(),
+                new Constructors.ST_GeomFromKML(),
                 new Functions.ST_Buffer(),
                 new Functions.ST_Distance(),
                 new Functions.ST_Transform(),
@@ -46,6 +48,8 @@ public class Catalog {
                 new Functions.ST_AsText(),
                 new Functions.ST_AsBinary(),
                 new Functions.ST_AsGeoJSON(),
+                new Functions.ST_AsGML(),
+                new Functions.ST_AsKML(),
                 new Functions.ST_Force_2D(),
                 new Functions.ST_IsEmpty(),
                 new Functions.ST_YMax(),
diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
index fc006e18..0f62291d 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
@@ -24,6 +24,8 @@ import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.io.WKBReader;
 import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.gml2.GMLReader;
+import org.locationtech.jts.io.kml.KMLReader;
 
 public class Constructors {
 
@@ -164,4 +166,23 @@ public class Constructors {
             return eval(value, null);
         }
     }
+
+    public static class ST_GeomFromGML extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("String") String gml) throws ParseException {
+            GMLReader reader = new GMLReader();
+            try {
+                return reader.read(gml, new GeometryFactory());
+            } catch (Exception e) {
+                throw new ParseException(e);
+            }
+        }
+    }
+
+    public static class ST_GeomFromKML extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("String") String kml) throws ParseException {
+            return new KMLReader().read(kml);
+        }
+    }
 }
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 f062857d..ee0b4177 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
@@ -165,6 +165,22 @@ public class Functions {
         }
     }
 
+    public static class ST_AsGML extends ScalarFunction {
+        @DataTypeHint("String")
+        public String eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.asGML(geom);
+        }
+    }
+
+    public static class ST_AsKML extends ScalarFunction {
+        @DataTypeHint("String")
+        public String eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.asKML(geom);
+        }
+    }
+
     public static class ST_Force_2D 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) {
diff --git a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
index c216bc2f..35578334 100644
--- a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
@@ -21,6 +21,7 @@ import org.junit.Test;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Polygon;
 import org.wololo.jts2geojson.GeoJSONReader;
 
 import java.util.ArrayList;
@@ -30,6 +31,7 @@ import java.util.List;
 import static org.apache.flink.table.api.Expressions.$;
 import static org.apache.flink.table.api.Expressions.call;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
@@ -232,4 +234,46 @@ public class ConstructorTest extends TestBase{
 
         assertEquals(expectedGeom, result);
     }
+
+    @Test
+    public void testGeomFromGML() {
+        List<Row> data = new ArrayList<>();
+        String gml =
+                "<gml:Polygon>\n" +
+                "  <gml:outerBoundaryIs>\n" +
+                "    <gml:LinearRing>\n" +
+                "      <gml:coordinates>\n" +
+                "        0.0,0.0 0.0,1.0 1.0,1.0 1.0,0.0 0.0,0.0\n" +
+                "      </gml:coordinates>\n" +
+                "    </gml:LinearRing>\n" +
+                "  </gml:outerBoundaryIs>\n" +
+                "</gml:Polygon>";
+        data.add(Row.of(gml, "polygon", 0L));
+
+        Table wktTable = createTextTable(data, polygonColNames);
+        Table geomTable = wktTable.select(call(Constructors.ST_GeomFromGML.class.getSimpleName(),
+                $(polygonColNames[0])).as(polygonColNames[0]),
+                $(polygonColNames[1]));
+        assertTrue(first(geomTable).getField(0) instanceof Polygon);
+    }
+
+    @Test
+    public void testGeomFromKML() {
+        List<Row> data = new ArrayList<>();
+        String kml =
+                "<Polygon>\n" +
+                "  <outerBoundaryIs>\n" +
+                "    <LinearRing>\n" +
+                "      <coordinates>0.0,0.0 0.0,1.0 1.0,1.0 1.0,0.0 0.0,0.0</coordinates>\n" +
+                "    </LinearRing>\n" +
+                "  </outerBoundaryIs>\n" +
+                "</Polygon>";
+        data.add(Row.of(kml, "polygon", 0L));
+
+        Table wktTable = createTextTable(data, polygonColNames);
+        Table geomTable = wktTable.select(call(Constructors.ST_GeomFromKML.class.getSimpleName(),
+                $(polygonColNames[0])).as(polygonColNames[0]),
+                $(polygonColNames[1]));
+        assertTrue(first(geomTable).getField(0) instanceof Polygon);
+    }
 }
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 1ee2a743..51987835 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -181,6 +181,40 @@ public class FunctionTest extends TestBase{
         assertEquals("01030000000100000005000000000000000000e0bf000000000000e0bf000000000000e0bf000000000000e03f000000000000e03f000000000000e03f000000000000e03f000000000000e0bf000000000000e0bf000000000000e0bf", result);
     }
 
+    @Test
+    public void testAsGML() throws Exception {
+        Table polygonTable = createPolygonTable(testDataSize);
+        polygonTable = polygonTable.select(call(Functions.ST_AsGML.class.getSimpleName(), $(polygonColNames[0])));
+        String result = (String) first(polygonTable).getField(0);
+        String expected =
+                "<gml:Polygon>\n" +
+                "  <gml:outerBoundaryIs>\n" +
+                "    <gml:LinearRing>\n" +
+                "      <gml:coordinates>\n" +
+                "        -0.5,-0.5 -0.5,0.5 0.5,0.5 0.5,-0.5 -0.5,-0.5 \n" +
+                "      </gml:coordinates>\n" +
+                "    </gml:LinearRing>\n" +
+                "  </gml:outerBoundaryIs>\n" +
+                "</gml:Polygon>\n";
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testAsKML() {
+        Table polygonTable = createPolygonTable(testDataSize);
+        polygonTable = polygonTable.select(call(Functions.ST_AsKML.class.getSimpleName(), $(polygonColNames[0])));
+        String result = (String) first(polygonTable).getField(0);
+        String expected =
+                "<Polygon>\n" +
+                "  <outerBoundaryIs>\n" +
+                "  <LinearRing>\n" +
+                "    <coordinates>-0.5,-0.5 -0.5,0.5 0.5,0.5 0.5,-0.5 -0.5,-0.5</coordinates>\n" +
+                "  </LinearRing>\n" +
+                "  </outerBoundaryIs>\n" +
+                "</Polygon>\n";
+        assertEquals(expected, result);
+    }
+
     @Test
     public void testGeoJSON() {
         Table polygonTable = createPolygonTable(testDataSize);
diff --git a/sql/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala b/sql/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index d0babdc1..3437c45b 100644
--- a/sql/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/sql/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -36,6 +36,8 @@ object Catalog {
     ST_GeomFromWKT,
     ST_GeomFromWKB,
     ST_GeomFromGeoJSON,
+    ST_GeomFromGML,
+    ST_GeomFromKML,
     ST_Point,
     ST_PolygonFromEnvelope,
     ST_Contains,
@@ -70,6 +72,8 @@ object Catalog {
     ST_AsGeoJSON,
     ST_AsBinary,
     ST_AsEWKB,
+    ST_AsGML,
+    ST_AsKML,
     ST_SRID,
     ST_SetSRID,
     ST_GeometryType,
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
index 5149f5f7..225c09d8 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
@@ -32,6 +32,8 @@ import org.apache.spark.sql.types.{BinaryType, DataType, Decimal, StringType}
 import org.apache.spark.unsafe.types.UTF8String
 import org.locationtech.jts.geom.{Coordinate, GeometryFactory}
 import org.locationtech.jts.io.WKBReader
+import org.locationtech.jts.io.gml2.GMLReader
+import org.locationtech.jts.io.kml.KMLReader
 
 /**
   * Return a point from a string. The string must be plain string and each coordinate must be separated by a delimiter.
@@ -436,6 +438,50 @@ case class ST_GeomFromGeoHash(inputExpressions: Seq[Expression])
 
   override def children: Seq[Expression] = inputExpressions
 
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
+case class ST_GeomFromGML(inputExpressions: Seq[Expression])
+  extends Expression with CodegenFallback {
+  assert(inputExpressions.length == 1)
+  override def nullable: Boolean = true
+
+  override def eval(inputRow: InternalRow): Any = {
+    (inputExpressions(0).eval(inputRow)) match {
+      case geomString: UTF8String =>
+        new GMLReader().read(geomString.toString, new GeometryFactory()).toGenericArrayData
+      case _ => null
+    }
+  }
+
+  override def dataType: DataType = GeometryUDT
+
+  override def children: Seq[Expression] = inputExpressions
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
+case class ST_GeomFromKML(inputExpressions: Seq[Expression])
+  extends Expression with CodegenFallback {
+  assert(inputExpressions.length == 1)
+  override def nullable: Boolean = true
+
+  override def eval(inputRow: InternalRow): Any = {
+    (inputExpressions(0).eval(inputRow)) match {
+      case geomString: UTF8String =>
+        new KMLReader().read(geomString.toString).toGenericArrayData
+      case _ => null
+    }
+  }
+
+  override def dataType: DataType = GeometryUDT
+
+  override def children: Seq[Expression] = inputExpressions
+
   protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
     copy(inputExpressions = newChildren)
   }
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 01f03e99..8564895e 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -1416,6 +1416,22 @@ case class ST_AsEWKT(inputExpressions: Seq[Expression])
   }
 }
 
+case class ST_AsGML(inputExpressions: Seq[Expression])
+  extends InferredUnaryExpression(Functions.asGML) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
+case class ST_AsKML(inputExpressions: Seq[Expression])
+  extends InferredUnaryExpression(Functions.asKML) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+    copy(inputExpressions = newChildren)
+  }
+}
+
 /**
  * Test if Geometry is empty geometry.
  *
diff --git a/sql/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala b/sql/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
index c1fc58aa..4a798cc2 100644
--- a/sql/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
+++ b/sql/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
@@ -22,7 +22,7 @@ package org.apache.sedona.sql
 import org.apache.sedona.core.formatMapper.GeoJsonReader
 import org.apache.sedona.core.formatMapper.shapefileParser.ShapefileReader
 import org.apache.sedona.sql.utils.Adapter
-import org.locationtech.jts.geom.Geometry
+import org.locationtech.jts.geom.{Geometry, LineString}
 
 class constructorTestScala extends TestBaseScala {
 
@@ -154,6 +154,34 @@ class constructorTestScala extends TestBaseScala {
       assert(polygonDf.count() == 1000)
     }
 
+    it("Passed ST_GeomFromGML") {
+      val linestring =
+        """'<gml:LineString srsName="EPSG:4269">
+          |    <gml:coordinates>
+          |        -71.16028,42.258729 -71.160837,42.259112 -71.161143,42.25932
+          |    </gml:coordinates>
+          |</gml:LineString>
+          |'""".stripMargin
+      val gml = sparkSession.sql(
+        s"""
+           |SELECT ST_GeomFromGML($linestring)
+           |""".stripMargin)
+      assert(gml.first().getAs[Geometry](0).isInstanceOf[LineString])
+    }
+
+    it("Passed ST_GeomFromKML") {
+      val linestring =
+        """'<LineString>
+          |<coordinates>-71.1663,42.2614 -71.1667,42.2616</coordinates>
+          |</LineString>
+          |'""".stripMargin
+      val kml = sparkSession.sql(
+        s"""
+           |SELECT ST_GeomFromKML($linestring)
+           |""".stripMargin)
+      assert(kml.first().getAs[Geometry](0).isInstanceOf[LineString])
+    }
+
     it("Passed GeoJsonReader to DataFrame") {
       var spatialRDD = GeoJsonReader.readToGeometryRDD(sparkSession.sparkContext, geojsonInputLocation)
       var spatialDf = Adapter.toDf(spatialRDD, sparkSession)
diff --git a/sql/src/test/scala/org/apache/sedona/sql/functionTestScala.scala b/sql/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index 5bdfa044..42389c70 100644
--- a/sql/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/sql/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -22,15 +22,18 @@ package org.apache.sedona.sql
 import org.apache.commons.codec.binary.Hex
 import org.apache.sedona.sql.implicits._
 import org.apache.spark.sql.functions._
-import org.apache.spark.sql.sedona_sql.expressions.ST_MakeValid
 import org.apache.spark.sql.{DataFrame, Row}
-import org.geotools.geometry.jts.WKTReader2
 import org.locationtech.jts.algorithm.MinimumBoundingCircle
 import org.locationtech.jts.geom.{Geometry, Polygon}
 import org.locationtech.jts.io.WKTWriter
 import org.locationtech.jts.linearref.LengthIndexedLine
 import org.locationtech.jts.operation.distance3d.Distance3DOp
 import org.scalatest.{GivenWhenThen, Matchers}
+import org.xml.sax.InputSource
+
+import java.io.StringReader
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.xpath.XPathFactory
 
 class functionTestScala extends TestBaseScala with Matchers with GeometrySample with GivenWhenThen {
 
@@ -313,6 +316,32 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample
       assert(Hex.encodeHexString(df.first().get(0).asInstanceOf[Array[Byte]]) == s)
     }
 
+    it("Passed ST_AsGML") {
+      val df = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON((1 1, 8 1, 8 8, 1 8, 1 1))') AS polygon")
+      df.createOrReplaceTempView("table")
+
+      val gmlDf = sparkSession.sql(
+        """select ST_AsGML(polygon) as geojson
+          |from table""".stripMargin)
+
+      val gml = DocumentBuilderFactory.newInstance.newDocumentBuilder.parse(new InputSource(new StringReader(gmlDf.first().getString(0))))
+      val coordinates = XPathFactory.newInstance.newXPath.evaluate("/Polygon/outerBoundaryIs/LinearRing/coordinates", gml)
+      assert(coordinates.trim === "1.0,1.0 8.0,1.0 8.0,8.0 1.0,8.0 1.0,1.0")
+    }
+
+    it("Passed ST_AsKML") {
+      val df = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON((1 1, 8 1, 8 8, 1 8, 1 1))') AS polygon")
+      df.createOrReplaceTempView("table")
+
+      val kmlDf = sparkSession.sql(
+        """select ST_AsKML(polygon) as geojson
+          |from table""".stripMargin)
+
+      val kml = DocumentBuilderFactory.newInstance.newDocumentBuilder.parse(new InputSource(new StringReader(kmlDf.first().getString(0))))
+      val coordinates = XPathFactory.newInstance.newXPath.evaluate("/Polygon/outerBoundaryIs/LinearRing/coordinates", kml)
+      assert(coordinates.trim === "1.0,1.0 8.0,1.0 8.0,8.0 1.0,8.0 1.0,1.0")
+    }
+
     it("Passed ST_SRID") {
       val df = sparkSession.sql("SELECT ST_SRID(ST_GeomFromWKT('POLYGON((1 1, 8 1, 8 8, 1 8, 1 1))'))")
       assert(df.first().getInt(0) == 0)