From 79b291c84b5ff342d465cc3b7ea33c74f2b6662c Mon Sep 17 00:00:00 2001 From: Nikolay Ulmasov Date: Mon, 15 May 2023 20:01:46 +0100 Subject: [PATCH] implement ST_ASEWKT refactor and add tests clean up restore ignored test --- docs/code-example-notebooks/accessors.scala | 27 +++++++++ docs/code-example-notebooks/kepler.py | 3 + docs/source/api/geometry-accessors.rst | 57 +++++++++++++++++++ python/mosaic/api/accessors.py | 20 +++++++ .../codegen/format/ConvertToCodeGen.scala | 1 + .../codegen/format/GeometryIOCodeGen.scala | 4 ++ .../format/MosaicGeometryIOCodeGenESRI.scala | 48 ++++++++++++++-- .../format/MosaicGeometryIOCodeGenJTS.scala | 41 +++++++++++-- .../mosaic/core/geometry/GeometryReader.scala | 1 + .../mosaic/core/geometry/GeometryWriter.scala | 2 + .../core/geometry/MosaicGeometryESRI.scala | 10 ++++ .../core/geometry/MosaicGeometryJTS.scala | 10 ++++ .../core/geometry/api/GeometryAPI.scala | 6 +- .../MosaicGeometryCollectionESRI.scala | 1 + .../MosaicGeometryCollectionJTS.scala | 2 + .../linestring/MosaicLineStringESRI.scala | 2 + .../linestring/MosaicLineStringJTS.scala | 2 + .../MosaicMultiLineStringESRI.scala | 2 + .../MosaicMultiLineStringJTS.scala | 2 + .../multipoint/MosaicMultiPointESRI.scala | 2 + .../multipoint/MosaicMultiPointJTS.scala | 2 + .../multipolygon/MosaicMultiPolygonESRI.scala | 2 + .../multipolygon/MosaicMultiPolygonJTS.scala | 2 + .../core/geometry/point/MosaicPointESRI.scala | 2 + .../core/geometry/point/MosaicPointJTS.scala | 2 + .../geometry/polygon/MosaicPolygonESRI.scala | 2 + .../geometry/polygon/MosaicPolygonJTS.scala | 2 + .../mosaic/expressions/format/ConvertTo.scala | 3 +- .../labs/mosaic/functions/MosaicContext.scala | 14 +++++ .../geometry/api/GeometryAPIBehaviors.scala | 5 ++ .../TestGeometryCollectionESRI.scala | 2 + .../TestGeometryCollectionJTS.scala | 2 + .../linestring/TestLineStringESRI.scala | 14 +++++ .../linestring/TestLineStringJTS.scala | 13 +++++ .../TestMultiLineStringESRI.scala | 13 +++++ .../TestMultiLineStringJTS.scala | 14 +++++ .../multipoint/TestMultiPointESRI.scala | 13 +++++ .../multipoint/TestMultiPointJTS.scala | 14 +++++ .../multipolygon/TestMultiPolygonESRI.scala | 15 ++++- .../multipolygon/TestMultiPolygonJTS.scala | 15 ++++- .../core/geometry/point/TestPointESRI.scala | 13 +++++ .../core/geometry/point/TestPointJTS.scala | 13 +++++ .../geometry/polygon/TestPolygonESRI.scala | 13 +++++ .../geometry/polygon/TestPolygonJTS.scala | 13 +++++ .../format/ConvertToBehaviors.scala | 6 ++ .../geometry/ConversionBehaviors.scala | 4 ++ .../functions/MosaicContextBehaviors.scala | 3 + 47 files changed, 446 insertions(+), 13 deletions(-) diff --git a/docs/code-example-notebooks/accessors.scala b/docs/code-example-notebooks/accessors.scala index 37981afef..6dd666b91 100644 --- a/docs/code-example-notebooks/accessors.scala +++ b/docs/code-example-notebooks/accessors.scala @@ -105,3 +105,30 @@ df.select(st_astext(st_point($"lon", $"lat")).alias("wkt")).show() // MAGIC %r // MAGIC df <- createDataFrame(data.frame(lon = 30.0, lat = 10.0)) // MAGIC showDF(select(df, alias(st_aswkt(st_point(column("lon"), column("lat"))), "wkt")), truncate=F) + +// COMMAND ---------- + +// MAGIC %md +// MAGIC ### st_asewkt + +// COMMAND ---------- + +// MAGIC %python +// MAGIC df = spark.createDataFrame([{'lon': 30., 'lat': 10.}]) +// MAGIC df.select(st_asewkt(st_point('lon', 'lat')).alias('ewkt')).show() + +// COMMAND ---------- + +val df = List((30.0, 10.0)).toDF("lon", "lat") +df.select(st_asewkt(st_point($"lon", $"lat")).alias("ewkt")).show() + +// COMMAND ---------- + +// MAGIC %sql +// MAGIC SELECT st_asewkt(st_point(30D, 10D)) AS ewkt + +// COMMAND ---------- + +// MAGIC %r +// MAGIC df <- createDataFrame(data.frame(lon = 30.0, lat = 10.0)) +// MAGIC showDF(select(df, alias(st_asewkt(st_point(column("lon"), column("lat"))), "ewkt")), truncate=F) diff --git a/docs/code-example-notebooks/kepler.py b/docs/code-example-notebooks/kepler.py index ca6bc5753..8a37552f3 100644 --- a/docs/code-example-notebooks/kepler.py +++ b/docs/code-example-notebooks/kepler.py @@ -74,6 +74,9 @@ # WKB representation .withColumn("geom_wkb", mos.st_aswkb(col("geom_internal"))) + + # WKT representation + .withColumn("geom_ewkt", mos.st_asewkt(col("geom_internal"))) # Limit to only 1 shape .limit(1) diff --git a/docs/source/api/geometry-accessors.rst b/docs/source/api/geometry-accessors.rst index a78e5a5aa..e880fe091 100644 --- a/docs/source/api/geometry-accessors.rst +++ b/docs/source/api/geometry-accessors.rst @@ -278,3 +278,60 @@ st_aswkt .. note:: Alias for :ref:`st_astext`. + +st_aswkt +******** + +.. function:: st_aeswkt(col) + + Translate a geometry into its representation in Extended Well-known Text (EWKT) format. + + :param col: Geometry column + :type col: Column: BinaryType, HexType, JSONType or InternalGeometryType + :rtype: Column: StringType + + :example: + +.. tabs:: + .. code-tab:: py + + >>> df = spark.createDataFrame([{'lon': 30., 'lat': 10.}]) + >>> df.select(st_asewkt(st_point('lon', 'lat')).alias('ewkt')).show() + +-----------------------+ + | ewkt| + +-----------------------+ + |SRID=4326;POINT (30 10)| + +-----------------------+ + + .. code-tab:: scala + + >>> val df = List((30.0, 10.0)).toDF("lon", "lat") + >>> df.select(st_asewkt(st_point($"lon", $"lat")).alias("ewkt")).show() + +-----------------------+ + | ewkt| + +-----------------------+ + |SRID=4326;POINT (30 10)| + +-----------------------+ + + .. code-tab:: sql + + >>> SELECT st_asewkt(st_point(30.0D, 10.0D)) AS ewkt + +-----------------------+ + | ewkt| + +-----------------------+ + |SRID=4326;POINT (30 10)| + +-----------------------+ + + .. code-tab:: r R + + >>> df <- createDataFrame(data.frame(lon = 30.0, lat = 10.0)) + >>> showDF(select(df, alias(st_asewkt(st_point(column("lon"), column("lat"))), "ewkt")), truncate=F) + +-----------------------+ + | ewkt| + +-----------------------+ + |SRID=4326;POINT (30 10)| + +-----------------------+ + + +.. note:: Default SRID value of a geometry created without specifying the explicit SRID value may be specific to a chosen geometry API. Currently, + default SRID on ESRI is 4326 (as shown in the examples), whereas it is 0 on JTS. \ No newline at end of file diff --git a/python/mosaic/api/accessors.py b/python/mosaic/api/accessors.py index abe1388b5..99d2db7b4 100644 --- a/python/mosaic/api/accessors.py +++ b/python/mosaic/api/accessors.py @@ -17,6 +17,7 @@ "as_hex", "as_json", "convert_to", + "st_asewkt", ] @@ -131,3 +132,22 @@ def convert_to(geom: ColumnOrName) -> Column: return config.mosaic_context.invoke_function( "convert_to", pyspark_to_java_column(geom) ) + +def st_asewkt(geom: ColumnOrName) -> Column: + """ + Translate a geometry into its Extended Well-known Text (EWKT) representation. + + Parameters + ---------- + geom : Column (BinaryType, HexType, JSONType or InternalGeometryType) + Geometry column + + Returns + ------- + Column (StringType) + An EWKT geometry + + """ + return config.mosaic_context.invoke_function( + "st_asewkt", pyspark_to_java_column(geom) + ) \ No newline at end of file diff --git a/src/main/scala/com/databricks/labs/mosaic/codegen/format/ConvertToCodeGen.scala b/src/main/scala/com/databricks/labs/mosaic/codegen/format/ConvertToCodeGen.scala index b238213d1..2680149bf 100644 --- a/src/main/scala/com/databricks/labs/mosaic/codegen/format/ConvertToCodeGen.scala +++ b/src/main/scala/com/databricks/labs/mosaic/codegen/format/ConvertToCodeGen.scala @@ -68,6 +68,7 @@ object ConvertToCodeGen { case "JSONOBJECT" => geometryCodeGen.toJSON(ctx, eval, geometryAPI) case "GEOJSON" => geometryCodeGen.toGeoJSON(ctx, eval, geometryAPI) case "COORDS" => geometryCodeGen.toInternal(ctx, eval, geometryAPI) + case "EWKT" => geometryCodeGen.toEWKT(ctx, eval, geometryAPI) case _ => throw new Error(s"Data type unsupported: $outputDataFormatName.") } } diff --git a/src/main/scala/com/databricks/labs/mosaic/codegen/format/GeometryIOCodeGen.scala b/src/main/scala/com/databricks/labs/mosaic/codegen/format/GeometryIOCodeGen.scala index 5a87efab9..ffdb4b254 100644 --- a/src/main/scala/com/databricks/labs/mosaic/codegen/format/GeometryIOCodeGen.scala +++ b/src/main/scala/com/databricks/labs/mosaic/codegen/format/GeometryIOCodeGen.scala @@ -16,6 +16,8 @@ trait GeometryIOCodeGen { def fromInternal(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) + def fromEWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) + def toWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) def toWKB(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) @@ -28,4 +30,6 @@ trait GeometryIOCodeGen { def toInternal(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) + def toEWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenESRI.scala b/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenESRI.scala index bbf4ebba1..bb8b0550d 100644 --- a/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenESRI.scala @@ -2,10 +2,11 @@ package com.databricks.labs.mosaic.codegen.format import java.nio.ByteBuffer -import com.databricks.labs.mosaic.core.geometry.MosaicGeometryESRI +import com.databricks.labs.mosaic.core.geometry.{MosaicGeometry, MosaicGeometryESRI} import com.databricks.labs.mosaic.core.geometry.api.GeometryAPI import com.databricks.labs.mosaic.core.types.InternalGeometryType import com.esri.core.geometry.ogc.OGCGeometry +import com.esri.core.geometry.SpatialReference import org.locationtech.jts.io.{WKBReader, WKBWriter} import org.apache.spark.sql.catalyst.expressions.GenericInternalRow @@ -15,9 +16,10 @@ import org.apache.spark.sql.types.{BinaryType, StringType} object MosaicGeometryIOCodeGenESRI extends GeometryIOCodeGen { override def fromWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { - val inputGeom = ctx.freshName("inputGeom") - val ogcGeom = classOf[OGCGeometry].getName - (s"""$ogcGeom $inputGeom = $ogcGeom.fromText($eval.toString());""", inputGeom) + // Technically, fromEWKT can have an implementation which is only a subset of implementation of + // fromWKT but it's not really necessary and both can use the same implementation so long as + // it works for both. + fromEWKT(ctx, eval, geometryAPI) } override def fromWKB(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { @@ -77,6 +79,30 @@ object MosaicGeometryIOCodeGenESRI extends GeometryIOCodeGen { ) } + override def fromEWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { + val inputGeom = ctx.freshName("inputGeom") + val geom = ctx.freshName("geom") + val parts = ctx.freshName("parts") + val srid = ctx.freshName("srid") + val ogcGeom = classOf[OGCGeometry].getName + val sptRef = classOf[SpatialReference].getName + ( + s""" + |$ogcGeom $inputGeom; + |String $geom = $eval.toString(); + |if ($geom.startsWith("SRID=")) { + | String[] $parts = $geom.split(";", 0); + | String $srid = $parts[0].split("=", 0)[1]; + | $inputGeom = $ogcGeom.fromText($parts[1]); + | $inputGeom.setSpatialReference($sptRef.create(Integer.parseInt($srid))); + |} else { + | $inputGeom = $ogcGeom.fromText($geom); + |} + |""".stripMargin, + inputGeom + ) + } + override def toWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { val outputGeom = ctx.freshName("outputGeom") val javaStringType = CodeGenerator.javaType(StringType) @@ -153,4 +179,18 @@ object MosaicGeometryIOCodeGenESRI extends GeometryIOCodeGen { ) } + override def toEWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { + val outputGeom = ctx.freshName("outputGeom") + val srid = ctx.freshName("grid") + val javaStringType = CodeGenerator.javaType(StringType) + ( + s""" + |int $srid = 0; + |if ($eval.esriSR != null) $srid = $eval.getEsriSpatialReference().getID(); + |$javaStringType $outputGeom = $javaStringType.fromString("SRID=" + Integer.toString($srid) + ";" + $eval.asText()); + |""".stripMargin, + outputGeom + ) + } + } diff --git a/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenJTS.scala b/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenJTS.scala index ffdfec5d3..fb44fae36 100644 --- a/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/codegen/format/MosaicGeometryIOCodeGenJTS.scala @@ -14,10 +14,7 @@ import org.apache.spark.sql.types.{BinaryType, StringType} object MosaicGeometryIOCodeGenJTS extends GeometryIOCodeGen { override def fromWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { - val inputGeom = ctx.freshName("inputGeom") - val jtsGeom = classOf[Geometry].getName - val wktReader = classOf[WKTReader].getName - (s"""$jtsGeom $inputGeom = new $wktReader().read($eval.toString());""", inputGeom) + fromEWKT(ctx, eval, geometryAPI) } override def fromWKB(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { @@ -78,6 +75,30 @@ object MosaicGeometryIOCodeGenJTS extends GeometryIOCodeGen { ) } + override def fromEWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { + val inputGeom = ctx.freshName("inputGeom") + val geom = ctx.freshName("geom") + val parts = ctx.freshName("parts") + val srid = ctx.freshName("srid") + val jtsGeom = classOf[Geometry].getName + val wktReader = classOf[WKTReader].getName + ( + s""" + |$jtsGeom $inputGeom; + |String $geom = $eval.toString(); + |if ($geom.startsWith("SRID=")) { + | String[] $parts = $geom.split(";", 0); + | String $srid = $parts[0].split("=", 0)[1]; + | $inputGeom = new $wktReader().read($parts[1]); + | $inputGeom.setSRID(Integer.parseInt($srid)); + |} else { + | $inputGeom = new $wktReader().read($geom);; + |} + |""".stripMargin, + inputGeom + ) + } + override def toWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { val outputGeom = ctx.freshName("outputGeom") val javaStringType = CodeGenerator.javaType(StringType) @@ -172,4 +193,16 @@ object MosaicGeometryIOCodeGenJTS extends GeometryIOCodeGen { ) } + override def toEWKT(ctx: CodegenContext, eval: String, geometryAPI: GeometryAPI): (String, String) = { + val outputGeom = ctx.freshName("outputGeom") + val javaStringType = CodeGenerator.javaType(StringType) + val wktWriterClass = classOf[WKTWriter].getName + ( + s""" + |$javaStringType $outputGeom = $javaStringType.fromString("SRID=" + Integer.toString($eval.getSRID()) + ";" + new $wktWriterClass().write($eval)); + |""".stripMargin, + outputGeom + ) + } + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryReader.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryReader.scala index 87266fc79..aa6363b15 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryReader.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryReader.scala @@ -19,4 +19,5 @@ trait GeometryReader { def fromSeq[T <: MosaicGeometry](geomSeq: Seq[T], geomType: GeometryTypeEnum.Value): MosaicGeometry + def fromEWKT(ewkt: String): MosaicGeometry } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryWriter.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryWriter.scala index 68758dd1a..d01c95a36 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryWriter.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/GeometryWriter.scala @@ -14,4 +14,6 @@ trait GeometryWriter { def toHEX: String + def toEWKT: String + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryESRI.scala index 3cf170289..9fb9b2fdd 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryESRI.scala @@ -164,6 +164,8 @@ abstract class MosaicGeometryESRI(geom: OGCGeometry) extends MosaicGeometry { override def toWKB: Array[Byte] = geom.asBinary().array() + override def toEWKT: String = s"SRID=${getSpatialReference};${toWKT}" + override def getSpatialReference: Int = if (geom.esriSR == null) 0 else geom.getEsriSpatialReference.getID override def setSpatialReference(srid: Int): Unit = { @@ -268,4 +270,12 @@ object MosaicGeometryESRI extends GeometryReader { reader(typeId).fromInternal(row).asInstanceOf[MosaicGeometryESRI] } + override def fromEWKT(ewkt: String): MosaicGeometryESRI = { + val pat = "SRID=(\\d*);(.*)".r + val pat(srid, wkt) = ewkt + val res = MosaicGeometryESRI(OGCGeometry.fromText(wkt)) + res.setSpatialReference(srid.toInt) + res + } + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryJTS.scala index 6cb8cac1c..5ef5da226 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/MosaicGeometryJTS.scala @@ -177,6 +177,8 @@ abstract class MosaicGeometryJTS(geom: Geometry) extends MosaicGeometry { override def toWKB: Array[Byte] = new WKBWriter().write(geom) + override def toEWKT: String = s"SRID=${getSpatialReference};${toWKT}" + override def numPoints: Int = geom.getNumPoints override def getSpatialReference: Int = geom.getSRID @@ -277,4 +279,12 @@ object MosaicGeometryJTS extends GeometryReader { case GEOMETRYCOLLECTION => MosaicGeometryCollectionJTS } + override def fromEWKT(ewkt: String): MosaicGeometryJTS = { + val pat = "SRID=(\\d*);(.*)".r + val pat(srid, wkt) = ewkt + val res = MosaicGeometryJTS(new WKTReader().read(wkt)) + res.setSpatialReference(srid.toInt) + res + } + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPI.scala index 7061608e0..878ebaa9b 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPI.scala @@ -23,6 +23,7 @@ abstract class GeometryAPI( case "HEX" => reader.fromHEX(input.asInstanceOf[String]) case "WKB" => reader.fromWKB(input.asInstanceOf[Array[Byte]]) case "GEOJSON" => reader.fromJSON(input.asInstanceOf[String]) + case "EWKT" => reader.fromEWKT(input.asInstanceOf[String]) case "COORDS" => throw new Error(s"$typeName not supported.") case _ => throw new Error(s"$typeName not supported.") } @@ -43,7 +44,7 @@ abstract class GeometryAPI( def geometry(inputData: InternalRow, dataType: DataType): MosaicGeometry = { dataType match { case _: BinaryType => reader.fromWKB(inputData.getBinary(0)) - case _: StringType => reader.fromWKT(inputData.getString(0)) + case _: StringType => val s = inputData.getString(0); if (s.matches("SRID=\\d*;.*")) reader.fromEWKT(s) else reader.fromWKT(s) case _: HexType => reader.fromHEX(inputData.get(0, HexType).asInstanceOf[InternalRow].getString(0)) case _: JSONType => reader.fromJSON(inputData.get(0, JSONType).asInstanceOf[InternalRow].getString(0)) case _: InternalGeometryType => reader.fromInternal(inputData.get(0, InternalGeometryType).asInstanceOf[InternalRow]) @@ -64,7 +65,7 @@ abstract class GeometryAPI( def geometry(inputData: Any, dataType: DataType): MosaicGeometry = dataType match { case _: BinaryType => reader.fromWKB(inputData.asInstanceOf[Array[Byte]]) - case _: StringType => reader.fromWKT(inputData.asInstanceOf[UTF8String].toString) + case _: StringType => val s = inputData.asInstanceOf[UTF8String].toString; if (s.matches("SRID=\\d*;.*")) reader.fromEWKT(s) else reader.fromWKT(s) case _: HexType => reader.fromHEX(inputData.asInstanceOf[InternalRow].getString(0)) case _: JSONType => reader.fromJSON(inputData.asInstanceOf[InternalRow].getString(0)) case _: InternalGeometryType => reader.fromInternal(inputData.asInstanceOf[InternalRow]) @@ -83,6 +84,7 @@ abstract class GeometryAPI( case "JSONOBJECT" => InternalRow.fromSeq(Seq(UTF8String.fromString(geometry.toJSON))) case "GEOJSON" => UTF8String.fromString(geometry.toJSON) case "COORDS" => geometry.toInternal.serialize + case "EWKT" => UTF8String.fromString(geometry.toEWKT) case _ => throw new Error(s"$dataFormatName not supported.") } } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionESRI.scala index dfdcb1bc2..c13715dac 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionESRI.scala @@ -202,4 +202,5 @@ object MosaicGeometryCollectionESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionJTS.scala index f0d224902..3f7622fec 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/MosaicGeometryCollectionJTS.scala @@ -201,4 +201,6 @@ object MosaicGeometryCollectionJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringESRI.scala index 0b08aa773..2e1b01e85 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringESRI.scala @@ -98,4 +98,6 @@ object MosaicLineStringESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringJTS.scala index ed2e9a04b..ce6672759 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/linestring/MosaicLineStringJTS.scala @@ -99,4 +99,6 @@ object MosaicLineStringJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringESRI.scala index 8938dd7cf..1376c7d85 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringESRI.scala @@ -118,4 +118,6 @@ object MosaicMultiLineStringESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringJTS.scala index 88b816ee3..6769deb4f 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/MosaicMultiLineStringJTS.scala @@ -95,4 +95,6 @@ object MosaicMultiLineStringJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointESRI.scala index fd2772868..60e31ab23 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointESRI.scala @@ -95,4 +95,6 @@ object MosaicMultiPointESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointJTS.scala index 52e9d98f4..300e32f33 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipoint/MosaicMultiPointJTS.scala @@ -87,4 +87,6 @@ object MosaicMultiPointJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonESRI.scala index f266f59c3..aad60f058 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonESRI.scala @@ -120,4 +120,6 @@ object MosaicMultiPolygonESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonJTS.scala index 9c1cca5fa..a81e51be1 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/MosaicMultiPolygonJTS.scala @@ -113,4 +113,6 @@ object MosaicMultiPolygonJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointESRI.scala index b57722edb..c7a6fa4d1 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointESRI.scala @@ -133,4 +133,6 @@ object MosaicPointESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointJTS.scala index 5234717b5..b9cf11ac1 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/point/MosaicPointJTS.scala @@ -117,4 +117,6 @@ object MosaicPointJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonESRI.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonESRI.scala index 5750e0f51..0d587ad48 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonESRI.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonESRI.scala @@ -110,4 +110,6 @@ object MosaicPolygonESRI extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryESRI = MosaicGeometryESRI.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryESRI = MosaicGeometryESRI.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonJTS.scala b/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonJTS.scala index 6487f2f66..5223e8ca6 100644 --- a/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonJTS.scala +++ b/src/main/scala/com/databricks/labs/mosaic/core/geometry/polygon/MosaicPolygonJTS.scala @@ -115,4 +115,6 @@ object MosaicPolygonJTS extends GeometryReader { override def fromHEX(hex: String): MosaicGeometryJTS = MosaicGeometryJTS.fromHEX(hex) + override def fromEWKT(ewkt: String): MosaicGeometryJTS = MosaicGeometryJTS.fromEWKT(ewkt) + } diff --git a/src/main/scala/com/databricks/labs/mosaic/expressions/format/ConvertTo.scala b/src/main/scala/com/databricks/labs/mosaic/expressions/format/ConvertTo.scala index b5c0fb667..31fe9e87d 100644 --- a/src/main/scala/com/databricks/labs/mosaic/expressions/format/ConvertTo.scala +++ b/src/main/scala/com/databricks/labs/mosaic/expressions/format/ConvertTo.scala @@ -37,7 +37,7 @@ case class ConvertTo(inGeometry: Expression, outDataType: String, geometryAPINam override def checkInputDataTypes(): TypeCheckResult = { val inputTypes = Seq(BinaryType, StringType, HexType, JSONType, InternalGeometryType) - val outputDataTypes = Seq("WKT", "WKB", "COORDS", "HEX", "GEOJSON", "JSONOBJECT") + val outputDataTypes = Seq("WKT", "WKB", "COORDS", "HEX", "GEOJSON", "JSONOBJECT", "EWKT") if (inputTypes.contains(inGeometry.dataType) && outputDataTypes.contains(outDataType.toUpperCase(Locale.ROOT))) { TypeCheckResult.TypeCheckSuccess @@ -57,6 +57,7 @@ case class ConvertTo(inGeometry: Expression, outDataType: String, geometryAPINam case "COORDS" => InternalGeometryType case "GEOJSON" => StringType case "JSONOBJECT" => JSONType + case "EWKT" => StringType case _ => throw new Error(s"Data type not supported: $outDataType") } diff --git a/src/main/scala/com/databricks/labs/mosaic/functions/MosaicContext.scala b/src/main/scala/com/databricks/labs/mosaic/functions/MosaicContext.scala index 5777cef8a..419f1e926 100644 --- a/src/main/scala/com/databricks/labs/mosaic/functions/MosaicContext.scala +++ b/src/main/scala/com/databricks/labs/mosaic/functions/MosaicContext.scala @@ -200,6 +200,11 @@ class MosaicContext(indexSystem: IndexSystem, geometryAPI: GeometryAPI, rasterAP ConvertTo.registryExpressionInfo(database, "st_geomfromgeojson"), (exprs: Seq[Expression]) => ConvertTo(AsJSON(exprs(0)), "coords", geometryAPI.name, Some("st_geomfromgeojson")) ) + registry.registerFunction( + FunctionIdentifier("st_geomfromewkt", database), + ConvertTo.registryExpressionInfo(database, "st_geomfromewkt"), + (exprs: Seq[Expression]) => ConvertTo(exprs(0), "coords", geometryAPI.name, Some("st_geomfromewkt")) + ) registry.registerFunction( FunctionIdentifier("convert_to_hex", database), ConvertTo.registryExpressionInfo(database, "convert_to_hex"), @@ -452,6 +457,12 @@ class MosaicContext(indexSystem: IndexSystem, geometryAPI: GeometryAPI, rasterAP (exprs: Seq[Expression]) => TrySql(exprs(0)) ) + registry.registerFunction( + FunctionIdentifier("st_asewkt", database), + ConvertTo.registryExpressionInfo(database, "st_aeswkt"), + (exprs: Seq[Expression]) => ConvertTo(exprs(0), "ewkt", geometryAPI.name, Some("st_asewkt")) + ) + /** Legacy API Specific aliases */ aliasFunction(registry, "index_geometry", database, "grid_boundaryaswkb", database) aliasFunction(registry, "mosaic_explode", database, "grid_tessellateexplode", database) @@ -566,6 +577,8 @@ class MosaicContext(indexSystem: IndexSystem, geometryAPI: GeometryAPI, rasterAP ColumnAdapter(ConvertTo(inGeom.expr, "coords", geometryAPI.name, Some("st_geomfromwkb"))) def st_geomfromgeojson(inGeom: Column): Column = ColumnAdapter(ConvertTo(AsJSON(inGeom.expr), "coords", geometryAPI.name, Some("st_geomfromgeojson"))) + def st_geomfromewkt(inGeom: Column): Column = + ColumnAdapter(ConvertTo(inGeom.expr, "coords", geometryAPI.name, Some("st_geomfromewkt"))) def st_makeline(points: Column): Column = ColumnAdapter(ST_MakeLine(points.expr, geometryAPI.name)) def st_makepolygon(boundaryRing: Column): Column = ColumnAdapter(ST_MakePolygon(boundaryRing.expr, array().expr)) def st_makepolygon(boundaryRing: Column, holeRingArray: Column): Column = @@ -577,6 +590,7 @@ class MosaicContext(indexSystem: IndexSystem, geometryAPI: GeometryAPI, rasterAP def st_astext(geom: Column): Column = ColumnAdapter(ConvertTo(geom.expr, "wkt", geometryAPI.name, Some("st_astext"))) def st_aswkb(geom: Column): Column = ColumnAdapter(ConvertTo(geom.expr, "wkb", geometryAPI.name, Some("st_aswkb"))) def st_aswkt(geom: Column): Column = ColumnAdapter(ConvertTo(geom.expr, "wkt", geometryAPI.name, Some("st_aswkt"))) + def st_asewkt(geom: Column): Column = ColumnAdapter(ConvertTo(geom.expr, "ewkt", geometryAPI.name, Some("st_asewkt"))) /** Spatial predicates */ def st_contains(geom1: Column, geom2: Column): Column = ColumnAdapter(ST_Contains(geom1.expr, geom2.expr, expressionConfig)) diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPIBehaviors.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPIBehaviors.scala index a836dcc4a..b3c82e60f 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPIBehaviors.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/api/GeometryAPIBehaviors.scala @@ -19,12 +19,14 @@ trait GeometryAPIBehaviors { this: AnyFunSuite => val hexRow = InternalRow.fromSeq(Seq(geometryAPI.serialize(point, HexType))) val geojsonRow = InternalRow.fromSeq(Seq(geometryAPI.serialize(point, JSONType))) val coordsRow = InternalRow.fromSeq(Seq(geometryAPI.serialize(point, InternalGeometryType))) + val ewktRow = InternalRow.fromSeq(Seq(UTF8String.fromString(point.toEWKT))) val wktSer = geometryAPI.serialize(point, "WKT") val wkbSer = geometryAPI.serialize(point, "WKB") val hexSer = geometryAPI.serialize(point, "HEX") val jsonSer = geometryAPI.serialize(point, "JSONOBJECT") val coordsSer = geometryAPI.serialize(point, "COORDS") + val ewktSer = geometryAPI.serialize(point, "EWKT") val wktSerDT = geometryAPI.serialize(point, StringType) val wkbSerDT = geometryAPI.serialize(point, BinaryType) @@ -44,6 +46,7 @@ trait GeometryAPIBehaviors { this: AnyFunSuite => geometryAPI.geometry(hexSer, HexType).equals(point) shouldEqual true geometryAPI.geometry(jsonSer, JSONType).equals(point) shouldEqual true geometryAPI.geometry(coordsSer, InternalGeometryType).equals(point) shouldEqual true + geometryAPI.geometry(ewktSer, StringType).equals(point) shouldEqual true geometryAPI.geometry(wktSerDT, StringType).equals(point) shouldEqual true geometryAPI.geometry(wkbSerDT, BinaryType).equals(point) shouldEqual true @@ -55,6 +58,7 @@ trait GeometryAPIBehaviors { this: AnyFunSuite => geometryAPI.geometry(point.toWKT, "WKT").equals(point) shouldEqual true geometryAPI.geometry(point.toHEX, "HEX").equals(point) shouldEqual true geometryAPI.geometry(point.toJSON, "GEOJSON").equals(point) shouldEqual true + geometryAPI.geometry(point.toEWKT, "EWKT").equals(point) shouldEqual true an[Error] should be thrownBy geometryAPI.geometry(point.toInternal, "COORDS") geometryAPI.geometry(wkbRow, BinaryType).equals(point) shouldEqual true @@ -62,6 +66,7 @@ trait GeometryAPIBehaviors { this: AnyFunSuite => geometryAPI.geometry(hexRow, HexType).equals(point) shouldEqual true geometryAPI.geometry(geojsonRow, JSONType).equals(point) shouldEqual true geometryAPI.geometry(coordsRow, InternalGeometryType).equals(point) shouldEqual true + geometryAPI.geometry(ewktRow, StringType).equals(point) shouldEqual true an[Error] should be thrownBy geometryAPI.geometry(wkbRow, IntegerType) } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionESRI.scala index d47027383..923cb6233 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionESRI.scala @@ -42,11 +42,13 @@ class TestGeometryCollectionESRI extends AnyFunSuite { val geomJSON = geometryCollection.toJSON val geomWKT = geometryCollection.toWKT val geomHex = geometryCollection.toHEX + val geomEWKT = geometryCollection.toEWKT MosaicGeometryCollectionESRI.fromWKB(geomWKB).equals(geometryCollection) shouldBe true MosaicGeometryCollectionESRI.fromJSON(geomJSON).equals(geometryCollection) shouldBe true MosaicGeometryCollectionESRI.fromWKT(geomWKT).equals(geometryCollection) shouldBe true MosaicGeometryCollectionESRI.fromHEX(geomHex).equals(geometryCollection) shouldBe true + MosaicGeometryCollectionESRI.fromEWKT(geomEWKT).equals(geometryCollection) shouldBe true } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionJTS.scala index d164cb61f..b8a107286 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/geometrycollection/TestGeometryCollectionJTS.scala @@ -43,11 +43,13 @@ class TestGeometryCollectionJTS extends AnyFunSuite { val geomJSON = geometryCollection.toJSON val geomWKT = geometryCollection.toWKT val geomHex = geometryCollection.toHEX + val geomEWKT = geometryCollection.toEWKT MosaicGeometryCollectionJTS.fromWKB(geomWKB).equals(geometryCollection) shouldBe true MosaicGeometryCollectionJTS.fromJSON(geomJSON).equals(geometryCollection) shouldBe true MosaicGeometryCollectionJTS.fromWKT(geomWKT).equals(geometryCollection) shouldBe true MosaicGeometryCollectionJTS.fromHEX(geomHex).equals(geometryCollection) shouldBe true + MosaicGeometryCollectionJTS.fromEWKT(geomEWKT).equals(geometryCollection) shouldBe true } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringESRI.scala index 0b451bd5e..6e97c8a3b 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringESRI.scala @@ -38,10 +38,12 @@ class TestLineStringESRI extends AnyFlatSpec { noException should be thrownBy MosaicLineStringESRI.fromHEX(lineString.toHEX) noException should be thrownBy MosaicLineStringESRI.fromJSON(lineString.toJSON) noException should be thrownBy MosaicLineStringESRI.fromInternal(lineString.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicLineStringESRI.fromEWKT(lineString.toEWKT) lineString.equals(MosaicLineStringESRI.fromWKB(lineString.toWKB)) shouldBe true lineString.equals(MosaicLineStringESRI.fromHEX(lineString.toHEX)) shouldBe true lineString.equals(MosaicLineStringESRI.fromJSON(lineString.toJSON)) shouldBe true lineString.equals(MosaicLineStringESRI.fromInternal(lineString.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + lineString.equals(MosaicLineStringESRI.fromEWKT(lineString.toEWKT)) shouldBe true } "MosaicLineStringESRI" should "be instantiable from a Seq of MosaicPointESRI" in { @@ -141,4 +143,16 @@ class TestLineStringESRI extends AnyFlatSpec { expectedResult.distance(testResult) should be < 0.001d } + "MosaicLineStringESRI" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "LINESTRING (1 1, 2 2, 3 3)" + val expectedEWKT = s"SRID=${srid};${wkt}" + val lineString = MosaicLineStringESRI.fromWKT(wkt).asInstanceOf[MosaicLineStringESRI] + lineString.setSpatialReference(srid) + + val ewkt = lineString.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicLineStringESRI.fromEWKT(ewkt).equals(lineString) shouldBe true + } + } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringJTS.scala index 2f0a5ef32..78ae3f1b6 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/linestring/TestLineStringJTS.scala @@ -32,10 +32,12 @@ class TestLineStringJTS extends AnyFlatSpec { noException should be thrownBy MosaicLineStringJTS.fromHEX(lineString.toHEX) noException should be thrownBy MosaicLineStringJTS.fromJSON(lineString.toJSON) noException should be thrownBy MosaicLineStringJTS.fromInternal(lineString.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicLineStringJTS.fromEWKT(lineString.toEWKT) lineString.equals(MosaicLineStringJTS.fromWKB(lineString.toWKB)) shouldBe true lineString.equals(MosaicLineStringJTS.fromHEX(lineString.toHEX)) shouldBe true lineString.equals(MosaicLineStringJTS.fromJSON(lineString.toJSON)) shouldBe true lineString.equals(MosaicLineStringJTS.fromInternal(lineString.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + lineString.equals(MosaicLineStringJTS.fromEWKT(lineString.toEWKT)) shouldBe true } "MosaicLineStringJTS" should "be instantiable from a Seq of MosaicPointJTS" in { @@ -135,4 +137,15 @@ class TestLineStringJTS extends AnyFlatSpec { expectedResult.distance(testResult) should be < 0.001d } + "MosaicLineStringJTS" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "LINESTRING (1 1, 2 2, 3 3)" + val expectedEWKT = s"SRID=${srid};${wkt}" + val lineString = MosaicLineStringJTS.fromWKT(wkt).asInstanceOf[MosaicLineStringJTS] + lineString.setSpatialReference(srid) + + val ewkt = lineString.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicLineStringJTS.fromEWKT(ewkt).equals(lineString) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringESRI.scala index 79c3d5e26..62d3d9501 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringESRI.scala @@ -36,12 +36,14 @@ class TestMultiLineStringESRI extends AnyFlatSpec { noException should be thrownBy MosaicMultiLineStringESRI.fromInternal( multiLineString.toInternal.serialize.asInstanceOf[InternalRow] ) + noException should be thrownBy MosaicMultiLineStringESRI.fromEWKT(multiLineString.toEWKT) multiLineString.equals(MosaicMultiLineStringESRI.fromWKB(multiLineString.toWKB)) shouldBe true multiLineString.equals(MosaicMultiLineStringESRI.fromHEX(multiLineString.toHEX)) shouldBe true multiLineString.equals(MosaicMultiLineStringESRI.fromJSON(multiLineString.toJSON)) shouldBe true multiLineString.equals( MosaicMultiLineStringESRI.fromInternal(multiLineString.toInternal.serialize.asInstanceOf[InternalRow]) ) shouldBe true + multiLineString.equals(MosaicMultiLineStringESRI.fromEWKT(multiLineString.toEWKT)) shouldBe true } "MosaicMultiLineStringESRI" should "be instantiable from a Seq of MosaicLineStringESRI" in { @@ -128,4 +130,15 @@ class TestMultiLineStringESRI extends AnyFlatSpec { multiLineString.mapXY({ (x: Double, y: Double) => (x * 2, y / 2) }).getSpatialReference shouldBe srid } + "MosaicMultiLineStringESRI" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))" + val expectedEWKT = s"SRID=${srid};${wkt}" + val multiLineString = MosaicMultiLineStringESRI.fromWKT(wkt).asInstanceOf[MosaicMultiLineStringESRI] + multiLineString.setSpatialReference(srid) + + val ewkt = multiLineString.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicMultiLineStringESRI.fromEWKT(ewkt).equals(multiLineString) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringJTS.scala index 8dfc870af..6b6e2fb32 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multilinestring/TestMultiLineStringJTS.scala @@ -34,12 +34,14 @@ class TestMultiLineStringJTS extends AnyFlatSpec { noException should be thrownBy MosaicMultiLineStringJTS.fromHEX(multiLineString.toHEX) noException should be thrownBy MosaicMultiLineStringJTS.fromJSON(multiLineString.toJSON) noException should be thrownBy MosaicMultiLineStringJTS.fromInternal(multiLineString.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicMultiLineStringJTS.fromEWKT(multiLineString.toEWKT) multiLineString.equals(MosaicMultiLineStringJTS.fromWKB(multiLineString.toWKB)) shouldBe true multiLineString.equals(MosaicMultiLineStringJTS.fromHEX(multiLineString.toHEX)) shouldBe true multiLineString.equals(MosaicMultiLineStringJTS.fromJSON(multiLineString.toJSON)) shouldBe true multiLineString.equals( MosaicMultiLineStringJTS.fromInternal(multiLineString.toInternal.serialize.asInstanceOf[InternalRow]) ) shouldBe true + multiLineString.equals(MosaicMultiLineStringJTS.fromEWKT(multiLineString.toEWKT)) shouldBe true } "MosaicMultiLineStringJTS" should "be instantiable from a Seq of MosaicLineStringJTS" in { @@ -126,4 +128,16 @@ class TestMultiLineStringJTS extends AnyFlatSpec { multiLineString.mapXY({ (x: Double, y: Double) => (x * 2, y / 2) }).getSpatialReference shouldBe srid } + "MosaicMultiLineStringJTS" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))" + val expectedEWKT = s"SRID=${srid};${wkt}" + val multiLineString = MosaicMultiLineStringJTS.fromWKT(wkt).asInstanceOf[MosaicMultiLineStringJTS] + multiLineString.setSpatialReference(srid) + + val ewkt = multiLineString.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicMultiLineStringJTS.fromEWKT(ewkt).equals(multiLineString) shouldBe true + } + } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointESRI.scala index 4cda2724a..3c713976a 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointESRI.scala @@ -36,10 +36,12 @@ class TestMultiPointESRI extends AnyFlatSpec { noException should be thrownBy MosaicMultiPointESRI.fromHEX(multiPoint.toHEX) noException should be thrownBy MosaicMultiPointESRI.fromJSON(multiPoint.toJSON) noException should be thrownBy MosaicMultiPointESRI.fromInternal(multiPoint.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicMultiPointESRI.fromEWKT(multiPoint.toEWKT) multiPoint.equals(MosaicMultiPointESRI.fromWKB(multiPoint.toWKB)) shouldBe true multiPoint.equals(MosaicMultiPointESRI.fromHEX(multiPoint.toHEX)) shouldBe true multiPoint.equals(MosaicMultiPointESRI.fromJSON(multiPoint.toJSON)) shouldBe true multiPoint.equals(MosaicMultiPointESRI.fromInternal(multiPoint.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + multiPoint.equals(MosaicMultiPointESRI.fromEWKT(multiPoint.toEWKT)) shouldBe true } "MosaicMultiPointESRI" should "be instantiable from a Seq of MosaicPointESRI" in { @@ -120,4 +122,15 @@ class TestMultiPointESRI extends AnyFlatSpec { multiPoint.mapXY({ (x: Double, y: Double) => (x * 2, y / 2) }).getSpatialReference shouldBe srid } + "MosaicMultiPointESRI" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val multiPoint = MosaicMultiPointESRI.fromWKT("MULTIPOINT (1 1, 2 2, 3 3)").asInstanceOf[MosaicMultiPointESRI] + val wkt = multiPoint.toWKT + val expectedEWKT = s"SRID=${srid};${wkt}" + multiPoint.setSpatialReference(srid) + + val ewkt = multiPoint.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicMultiPointESRI.fromEWKT(ewkt).equals(multiPoint) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointJTS.scala index f26ea529e..246be9b59 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipoint/TestMultiPointJTS.scala @@ -36,10 +36,12 @@ class TestMultiPointJTS extends AnyFlatSpec { noException should be thrownBy MosaicMultiPointJTS.fromHEX(multiPoint.toHEX) noException should be thrownBy MosaicMultiPointJTS.fromJSON(multiPoint.toJSON) noException should be thrownBy MosaicMultiPointJTS.fromInternal(multiPoint.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicMultiPointJTS.fromEWKT(multiPoint.toEWKT) multiPoint.equals(MosaicMultiPointJTS.fromWKB(multiPoint.toWKB)) shouldBe true multiPoint.equals(MosaicMultiPointJTS.fromHEX(multiPoint.toHEX)) shouldBe true multiPoint.equals(MosaicMultiPointJTS.fromJSON(multiPoint.toJSON)) shouldBe true multiPoint.equals(MosaicMultiPointJTS.fromInternal(multiPoint.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + multiPoint.equals(MosaicMultiPointJTS.fromEWKT(multiPoint.toEWKT)) shouldBe true } "MosaicMultiPointJTS" should "be instantiable from a Seq of MosaicPointJTS" in { @@ -121,4 +123,16 @@ class TestMultiPointJTS extends AnyFlatSpec { multiPoint.mapXY({ (x: Double, y: Double) => (x * 2, y / 2) }).getSpatialReference shouldBe srid } + "MosaicMultiPointJTS" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val multiPoint = MosaicMultiPointJTS.fromWKT("MULTIPOINT (1 1, 2 2, 3 3)").asInstanceOf[MosaicMultiPointJTS] + val wkt = multiPoint.toWKT + val expectedEWKT = s"SRID=${srid};${wkt}" + multiPoint.setSpatialReference(srid) + + val ewkt = multiPoint.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicMultiPointJTS.fromEWKT(ewkt).equals(multiPoint) shouldBe true + } + } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonESRI.scala index 6c98a8d68..753245bf2 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonESRI.scala @@ -32,10 +32,12 @@ class TestMultiPolygonESRI extends AnyFlatSpec { noException should be thrownBy MosaicMultiPolygonESRI.fromHEX(multiPolygon.toHEX) noException should be thrownBy MosaicMultiPolygonESRI.fromJSON(multiPolygon.toJSON) noException should be thrownBy MosaicMultiPolygonESRI.fromInternal(multiPolygon.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicMultiPolygonESRI.fromEWKT(multiPolygon.toEWKT) multiPolygon.equals(MosaicMultiPolygonESRI.fromWKB(multiPolygon.toWKB)) shouldBe true multiPolygon.equals(MosaicMultiPolygonESRI.fromHEX(multiPolygon.toHEX)) shouldBe true multiPolygon.equals(MosaicMultiPolygonESRI.fromJSON(multiPolygon.toJSON)) shouldBe true multiPolygon.equals(MosaicMultiPolygonESRI.fromInternal(multiPolygon.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + multiPolygon.equals(MosaicMultiPolygonESRI.fromEWKT(multiPolygon.toEWKT)) shouldBe true } "MosaicMultiPolygonESRI" should "load holes correctly" in { @@ -110,7 +112,7 @@ class TestMultiPolygonESRI extends AnyFlatSpec { results should contain only true } - "MosaicPolygonESRI" should "maintain SRID across operations" in { + "MosaicMultiPolygonESRI" should "maintain SRID across operations" in { val srid = 32632 val multiPolygon = MosaicMultiPolygonESRI .fromWKT( @@ -145,4 +147,15 @@ class TestMultiPolygonESRI extends AnyFlatSpec { multiPolygon.mapXY({ (x: Double, y: Double) => (x * 2, y / 2) }).getSpatialReference shouldBe srid } + "MosaicMultiPolygonESRI" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))" + val expectedEWKT = s"SRID=${srid};${wkt}" + val multiPolygon = MosaicMultiPolygonESRI.fromWKT(wkt).asInstanceOf[MosaicMultiPolygonESRI] + multiPolygon.setSpatialReference(srid) + + val ewkt = multiPolygon.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicMultiPolygonESRI.fromEWKT(ewkt).equals(multiPolygon) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonJTS.scala index 40be47982..59eeaa8f1 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/multipolygon/TestMultiPolygonJTS.scala @@ -32,10 +32,12 @@ class TestMultiPolygonJTS extends AnyFlatSpec { noException should be thrownBy MosaicMultiPolygonJTS.fromHEX(multiPolygon.toHEX) noException should be thrownBy MosaicMultiPolygonJTS.fromJSON(multiPolygon.toJSON) noException should be thrownBy MosaicMultiPolygonJTS.fromInternal(multiPolygon.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicMultiPolygonJTS.fromEWKT(multiPolygon.toEWKT) multiPolygon.equals(MosaicMultiPolygonJTS.fromWKB(multiPolygon.toWKB)) shouldBe true multiPolygon.equals(MosaicMultiPolygonJTS.fromHEX(multiPolygon.toHEX)) shouldBe true multiPolygon.equals(MosaicMultiPolygonJTS.fromJSON(multiPolygon.toJSON)) shouldBe true multiPolygon.equals(MosaicMultiPolygonJTS.fromInternal(multiPolygon.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + multiPolygon.equals(MosaicMultiPolygonJTS.fromEWKT(multiPolygon.toEWKT)) shouldBe true } "MosaicMultiPolygonJTS" should "be instantiable from a Seq of MosaicPolygonJTS" in { @@ -103,7 +105,7 @@ class TestMultiPolygonJTS extends AnyFlatSpec { results should contain only true } - "MosaicPolygonJTS" should "maintain SRID across operations" in { + "MosaicMultiPolygonJTS" should "maintain SRID across operations" in { val srid = 32632 val multiPolygon = MosaicMultiPolygonJTS .fromWKT( @@ -138,4 +140,15 @@ class TestMultiPolygonJTS extends AnyFlatSpec { multiPolygon.mapXY({ (x: Double, y: Double) => (x * 2, y / 2) }).getSpatialReference shouldBe srid } + "MosaicMultiPolygonJTS" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))" + val expectedEWKT = s"SRID=${srid};${wkt}" + val multiPolygon = MosaicMultiPolygonJTS.fromWKT(wkt).asInstanceOf[MosaicMultiPolygonJTS] + multiPolygon.setSpatialReference(srid) + + val ewkt = multiPolygon.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicMultiPolygonJTS.fromEWKT(ewkt).equals(multiPolygon) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointESRI.scala index 60e59dd3c..4477ae80b 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointESRI.scala @@ -50,10 +50,12 @@ class TestPointESRI extends AnyFlatSpec { noException should be thrownBy MosaicPointESRI.fromHEX(point.toHEX) noException should be thrownBy MosaicPointESRI.fromJSON(point.toJSON) noException should be thrownBy MosaicPointESRI.fromInternal(point.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicPointESRI.fromEWKT(point.toEWKT) point.equals(MosaicPointESRI.fromWKB(point.toWKB)) shouldBe true point.equals(MosaicPointESRI.fromHEX(point.toHEX)) shouldBe true point.equals(MosaicPointESRI.fromJSON(point.toJSON)) shouldBe true point.equals(MosaicPointESRI.fromInternal(point.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + point.equals(MosaicPointESRI.fromEWKT(point.toEWKT)) shouldBe true } "MosaicPointESRI" should "maintain SRID across operations" in { @@ -95,4 +97,15 @@ class TestPointESRI extends AnyFlatSpec { comparison should contain only true } + "MosaicPointESRI" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "POINT (1 2)" + val expectedEWKT = s"SRID=${srid};${wkt}" + val point = MosaicPointESRI.fromWKT(wkt).asInstanceOf[MosaicPointESRI] + point.setSpatialReference(srid) + + val ewkt = point.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicPointESRI.fromEWKT(ewkt).equals(point) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointJTS.scala index 40b5009b2..5c934e84b 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/point/TestPointJTS.scala @@ -50,10 +50,12 @@ class TestPointJTS extends AnyFlatSpec { noException should be thrownBy MosaicPointJTS.fromHEX(point.toHEX) noException should be thrownBy MosaicPointJTS.fromJSON(point.toJSON) noException should be thrownBy MosaicPointJTS.fromInternal(point.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicPointJTS.fromEWKT(point.toEWKT) point.equals(MosaicPointJTS.fromWKB(point.toWKB)) shouldBe true point.equals(MosaicPointJTS.fromHEX(point.toHEX)) shouldBe true point.equals(MosaicPointJTS.fromJSON(point.toJSON)) shouldBe true point.equals(MosaicPointJTS.fromInternal(point.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + point.equals(MosaicPointJTS.fromEWKT(point.toEWKT)) shouldBe true } "MosaicPointJTS" should "maintain SRID across operations" in { @@ -95,4 +97,15 @@ class TestPointJTS extends AnyFlatSpec { comparison.take(2) should contain only true } + "MosaicPointJTS" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "POINT (1 2)" + val expectedEWKT = s"SRID=${srid};${wkt}" + val point = MosaicPointJTS.fromWKT(wkt).asInstanceOf[MosaicPointJTS] + point.setSpatialReference(srid) + + val ewkt = point.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicPointJTS.fromEWKT(ewkt).equals(point) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonESRI.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonESRI.scala index dc75e6132..77e81c8fb 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonESRI.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonESRI.scala @@ -34,10 +34,12 @@ class TestPolygonESRI extends AnyFlatSpec { noException should be thrownBy MosaicPolygonESRI.fromHEX(polygon.toHEX) noException should be thrownBy MosaicPolygonESRI.fromJSON(polygon.toJSON) noException should be thrownBy MosaicPolygonESRI.fromInternal(polygon.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicPolygonESRI.fromEWKT(polygon.toEWKT) polygon.equals(MosaicPolygonESRI.fromWKB(polygon.toWKB)) shouldBe true polygon.equals(MosaicPolygonESRI.fromHEX(polygon.toHEX)) shouldBe true polygon.equals(MosaicPolygonESRI.fromJSON(polygon.toJSON)) shouldBe true polygon.equals(MosaicPolygonESRI.fromInternal(polygon.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + polygon.equals(MosaicPointESRI.fromEWKT(polygon.toEWKT)) shouldBe true } "MosaicPolygonESRI" should "be instantiable from a Seq of MosaicPointESRI" in { @@ -151,4 +153,15 @@ class TestPolygonESRI extends AnyFlatSpec { intersection.getArea shouldBe expectedResult.getArea +- 1d } + "MosaicPolygonESRI" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "POLYGON ((0 1, 3 0, 4 3, 0 4, 0 1))" + val expectedEWKT = s"SRID=${srid};${wkt}" + val polygon = MosaicPolygonESRI.fromWKT(wkt).asInstanceOf[MosaicPolygonESRI] + polygon.setSpatialReference(srid) + + val ewkt = polygon.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicPolygonESRI.fromEWKT(ewkt).equals(polygon) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonJTS.scala b/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonJTS.scala index 498e2f84f..f462e5001 100644 --- a/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonJTS.scala +++ b/src/test/scala/com/databricks/labs/mosaic/core/geometry/polygon/TestPolygonJTS.scala @@ -34,10 +34,12 @@ class TestPolygonJTS extends AnyFlatSpec { noException should be thrownBy MosaicPolygonJTS.fromHEX(polygon.toHEX) noException should be thrownBy MosaicPolygonJTS.fromJSON(polygon.toJSON) noException should be thrownBy MosaicPolygonJTS.fromInternal(polygon.toInternal.serialize.asInstanceOf[InternalRow]) + noException should be thrownBy MosaicPolygonJTS.fromEWKT(polygon.toEWKT) polygon.equals(MosaicPolygonJTS.fromWKB(polygon.toWKB)) shouldBe true polygon.equals(MosaicPolygonJTS.fromHEX(polygon.toHEX)) shouldBe true polygon.equals(MosaicPolygonJTS.fromJSON(polygon.toJSON)) shouldBe true polygon.equals(MosaicPolygonJTS.fromInternal(polygon.toInternal.serialize.asInstanceOf[InternalRow])) shouldBe true + polygon.equals(MosaicPolygonJTS.fromEWKT(polygon.toEWKT)) shouldBe true } "MosaicPolygonJTS" should "be instantiable from a Seq of MosaicPointJTS" in { @@ -151,4 +153,15 @@ class TestPolygonJTS extends AnyFlatSpec { intersection.getArea shouldBe expectedResult.getArea +- 1d } + "MosaicPolygonJTS" should "maintain SRID after EWKT conversion" in { + val srid = 32632 + val wkt = "POLYGON ((0 1, 3 0, 4 3, 0 4, 0 1))" + val expectedEWKT = s"SRID=${srid};${wkt}" + val polygon = MosaicPolygonJTS.fromWKT(wkt).asInstanceOf[MosaicPolygonJTS] + polygon.setSpatialReference(srid) + + val ewkt = polygon.toEWKT + ewkt.equals(expectedEWKT) shouldBe true + MosaicPolygonJTS.fromEWKT(ewkt).equals(polygon) shouldBe true + } } diff --git a/src/test/scala/com/databricks/labs/mosaic/expressions/format/ConvertToBehaviors.scala b/src/test/scala/com/databricks/labs/mosaic/expressions/format/ConvertToBehaviors.scala index 614d860f9..812afb61e 100644 --- a/src/test/scala/com/databricks/labs/mosaic/expressions/format/ConvertToBehaviors.scala +++ b/src/test/scala/com/databricks/labs/mosaic/expressions/format/ConvertToBehaviors.scala @@ -22,6 +22,7 @@ trait ConvertToBehaviors extends QueryTest { val hexes = getHexRowsDf(mc).select("hex") val geojsons = getGeoJSONDf(mc).select("geojson") val coords = getWKTRowsDf().select(st_geomfromwkt(col("wkt")).alias("coords")) + val ewkts = getWKTRowsDf().select(st_asewkt(col("wkt")).alias("ewkt")) val wkbExpr = wkbs.col("wkb").expr wkbExpr.checkInputDataTypes() shouldEqual TypeCheckSuccess @@ -31,26 +32,31 @@ trait ConvertToBehaviors extends QueryTest { ConvertTo(wkbs.col("wkb").expr, "COORDS", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkbs.col("wkb").expr, "HEX", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkbs.col("wkb").expr, "GEOJSON", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess + ConvertTo(wkbs.col("wkb").expr, "EWKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkts.col("wkt").expr, "WKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkts.col("wkt").expr, "WKB", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkts.col("wkt").expr, "COORDS", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkts.col("wkt").expr, "HEX", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(wkts.col("wkt").expr, "GEOJSON", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess + ConvertTo(wkts.col("wkt").expr, "EWKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(hexes.col("hex").expr, "WKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(hexes.col("hex").expr, "WKB", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(hexes.col("hex").expr, "COORDS", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(hexes.col("hex").expr, "HEX", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(hexes.col("hex").expr, "GEOJSON", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess + ConvertTo(hexes.col("hex").expr, "EWKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(geojsons.col("geojson").expr, "WKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(geojsons.col("geojson").expr, "WKB", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(geojsons.col("geojson").expr, "COORDS", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(geojsons.col("geojson").expr, "HEX", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(geojsons.col("geojson").expr, "GEOJSON", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess + ConvertTo(geojsons.col("geojson").expr, "EWKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(coords.col("coords").expr, "WKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(coords.col("coords").expr, "WKB", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(coords.col("coords").expr, "COORDS", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(coords.col("coords").expr, "HEX", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(coords.col("coords").expr, "GEOJSON", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess + ConvertTo(coords.col("coords").expr, "EWKT", geometryAPI.name).checkInputDataTypes() shouldEqual TypeCheckSuccess ConvertTo(lit(1).expr, "GEOJSON", geometryAPI.name).checkInputDataTypes().isFailure shouldEqual true an[Error] should be thrownBy ConvertTo(coords.col("coords").expr, "ERROR", geometryAPI.name) .checkInputDataTypes() diff --git a/src/test/scala/com/databricks/labs/mosaic/expressions/geometry/ConversionBehaviors.scala b/src/test/scala/com/databricks/labs/mosaic/expressions/geometry/ConversionBehaviors.scala index 042bdf20f..407483837 100644 --- a/src/test/scala/com/databricks/labs/mosaic/expressions/geometry/ConversionBehaviors.scala +++ b/src/test/scala/com/databricks/labs/mosaic/expressions/geometry/ConversionBehaviors.scala @@ -35,6 +35,8 @@ trait ConversionBehaviors { this: AnyFlatSpec => .withColumn("geom", expr("st_geomfromwkb(wkb)")) // WKB -> Geom .withColumn("geojson", expr("st_asgeojson(geom)")) // Geom -> GeoJSON .withColumn("geom", expr("st_geomfromgeojson(geojson)")) // GeoJSON -> Geom + .withColumn("ewkt", expr("st_asewkt(geom)")) // Geom -> EWKT + .withColumn("geom", expr("st_geomfromewkt(ewkt)")) // EWKT -> Geom .withColumn("new_wkt", expr("st_aswkt(geom)")) // Geom -> WKT .select("new_wkt") .collect() @@ -69,6 +71,8 @@ trait ConversionBehaviors { this: AnyFlatSpec => .withColumn("geom", st_geomfromwkb(col("wkb"))) // WKB -> Geom .withColumn("geojson", st_asgeojson(col("geom"))) // Geom -> GeoJSON .withColumn("geom", st_geomfromgeojson(col("geojson"))) // GeoJSON -> Geom + .withColumn("ewkt", expr("st_asewkt(geom)")) // Geom -> EWKT + .withColumn("geom", expr("st_geomfromewkt(ewkt)")) // EWKT -> Geom .withColumn("new_wkt", st_aswkt(col("geom"))) // Geom -> WKT .select("new_wkt") .collect() diff --git a/src/test/scala/com/databricks/labs/mosaic/functions/MosaicContextBehaviors.scala b/src/test/scala/com/databricks/labs/mosaic/functions/MosaicContextBehaviors.scala index ef87b5947..046d91473 100644 --- a/src/test/scala/com/databricks/labs/mosaic/functions/MosaicContextBehaviors.scala +++ b/src/test/scala/com/databricks/labs/mosaic/functions/MosaicContextBehaviors.scala @@ -49,6 +49,7 @@ trait MosaicContextBehaviors extends MosaicSpatialQueryTest { def getFunc(name: String): registry.FunctionBuilder = registry.lookupFunctionBuilder(FunctionIdentifier(name)).get val pointWkt = Literal("POINT (1 1 1)") + val pointEwkt = Literal("SRID=4326;POINT (1 1 1)") val pointsWkt = array(lit("POINT (1 1)"), lit("POINT (1 2)"), lit("POINT (2 3)")).expr val polygonPointsWkt = array(lit("POINT (1 1)"), lit("POINT (1 2)"), lit("POINT (2 3)"), lit("POINT (1 1)")).expr val holePointsWkt = array(lit("POINT (1.1 1.1)"), lit("POINT (1.1 1.9)"), lit("POINT (1.1 1.1)")).expr @@ -91,6 +92,7 @@ trait MosaicContextBehaviors extends MosaicSpatialQueryTest { noException should be thrownBy getFunc("st_geomfromwkt").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("st_geomfromwkb").apply(Seq(st_aswkb(multiPolygon).expr)) noException should be thrownBy getFunc("st_geomfromgeojson").apply(Seq(st_asgeojson(multiPolygon).expr)) + noException should be thrownBy getFunc("st_geomfromewkt").apply(Seq(pointEwkt)) noException should be thrownBy getFunc("convert_to_hex").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("convert_to_wkt").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("convert_to_wkb").apply(Seq(multiPolygon.expr)) @@ -101,6 +103,7 @@ trait MosaicContextBehaviors extends MosaicSpatialQueryTest { noException should be thrownBy getFunc("st_aswkb").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("st_asbinary").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("st_asgeojson").apply(Seq(multiPolygon.expr)) + noException should be thrownBy getFunc("st_asewkt").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("st_length").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("st_perimeter").apply(Seq(multiPolygon.expr)) noException should be thrownBy getFunc("st_distance").apply(Seq(multiPolygon.expr, pointWkt))