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 2023/04/01 07:28:08 UTC
[sedona] branch master updated: [SEDONA-271] Add raster function RS_SRID (#812)
This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new b987b423 [SEDONA-271] Add raster function RS_SRID (#812)
b987b423 is described below
commit b987b423a32e9105b7e50e087a28e5d85edd7573
Author: Martin Andersson <u....@gmail.com>
AuthorDate: Sat Apr 1 09:28:02 2023 +0200
[SEDONA-271] Add raster function RS_SRID (#812)
---
.../org/apache/sedona/common/raster/Functions.java | 22 +++++++++++++++++++--
.../sedona/common/raster/ConstructorsTest.java | 5 +++--
.../apache/sedona/common/raster/FunctionsTest.java | 11 ++++++++++-
docs/api/sql/Raster-operators.md | 18 +++++++++++++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sedona_sql/expressions/raster/Functions.scala | 23 ++++++++++++++++++++++
.../org/apache/sedona/sql/rasteralgebraTest.scala | 11 +++++++++++
7 files changed, 86 insertions(+), 5 deletions(-)
diff --git a/common/src/main/java/org/apache/sedona/common/raster/Functions.java b/common/src/main/java/org/apache/sedona/common/raster/Functions.java
index 489bd1d7..a7761bb4 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/Functions.java
@@ -18,30 +18,48 @@ import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.Envelope2D;
+import org.geotools.referencing.CRS;
+import org.geotools.referencing.crs.DefaultEngineeringCRS;
import org.locationtech.jts.geom.*;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import java.awt.image.Raster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.function.DoublePredicate;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
public class Functions {
- public static Geometry envelope(GridCoverage2D raster) {
+ public static Geometry envelope(GridCoverage2D raster) throws FactoryException {
Envelope2D envelope2D = raster.getEnvelope2D();
Envelope envelope = new Envelope(envelope2D.getMinX(), envelope2D.getMaxX(), envelope2D.getMinY(), envelope2D.getMaxY());
- return new GeometryFactory().toGeometry(envelope);
+ int srid = srid(raster);
+ return new GeometryFactory(new PrecisionModel(), srid).toGeometry(envelope);
}
public static int numBands(GridCoverage2D raster) {
return raster.getNumSampleDimensions();
}
+ public static int srid(GridCoverage2D raster) throws FactoryException {
+ CoordinateReferenceSystem crs = raster.getCoordinateReferenceSystem();
+ if (crs instanceof DefaultEngineeringCRS) {
+ // GeoTools defaults to internal non-standard epsg codes, like 404000, if crs is missing.
+ // We need to check for this case and return 0 instead.
+ if (((DefaultEngineeringCRS) crs).isWildcard()) {
+ return 0;
+ }
+ }
+ return Optional.ofNullable(CRS.lookupEpsgCode(crs, true)).orElse(0);
+ }
+
public static Double value(GridCoverage2D rasterGeom, Geometry geometry, int band) throws TransformException {
return values(rasterGeom, Collections.singletonList(geometry), band).get(0);
}
diff --git a/common/src/test/java/org/apache/sedona/common/raster/ConstructorsTest.java b/common/src/test/java/org/apache/sedona/common/raster/ConstructorsTest.java
index 69aa085b..f2ec505b 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/ConstructorsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/ConstructorsTest.java
@@ -16,6 +16,7 @@ package org.apache.sedona.common.raster;
import org.geotools.coverage.grid.GridCoverage2D;
import org.junit.Test;
import org.locationtech.jts.geom.Geometry;
+import org.opengis.referencing.FactoryException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -25,7 +26,7 @@ import static org.junit.Assert.*;
public class ConstructorsTest extends RasterTestBase {
@Test
- public void fromArcInfoAsciiGrid() throws IOException {
+ public void fromArcInfoAsciiGrid() throws IOException, FactoryException {
GridCoverage2D gridCoverage2D = Constructors.fromArcInfoAsciiGrid(arc.getBytes(StandardCharsets.UTF_8));
Geometry envelope = Functions.envelope(gridCoverage2D);
@@ -39,7 +40,7 @@ public class ConstructorsTest extends RasterTestBase {
}
@Test
- public void fromGeoTiff() throws IOException {
+ public void fromGeoTiff() throws IOException, FactoryException {
GridCoverage2D gridCoverage2D = Constructors.fromGeoTiff(geoTiff);
Geometry envelope = Functions.envelope(gridCoverage2D);
diff --git a/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java b/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
index 387d1448..f9b3a40b 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/FunctionsTest.java
@@ -18,6 +18,7 @@ import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
+import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import java.util.Arrays;
@@ -29,11 +30,13 @@ import static org.junit.Assert.*;
public class FunctionsTest extends RasterTestBase {
@Test
- public void envelope() {
+ public void envelope() throws FactoryException {
Geometry envelope = Functions.envelope(oneBandRaster);
assertEquals(3600.0d, envelope.getArea(), 0.1d);
assertEquals(378922.0d + 30.0d, envelope.getCentroid().getX(), 0.1d);
assertEquals(4072345.0d + 30.0d, envelope.getCentroid().getY(), 0.1d);
+
+ assertEquals(4326, Functions.envelope(multiBandRaster).getSRID());
}
@Test
@@ -42,6 +45,12 @@ public class FunctionsTest extends RasterTestBase {
assertEquals(4, Functions.numBands(multiBandRaster));
}
+ @Test
+ public void testSrid() throws FactoryException {
+ assertEquals(0, Functions.srid(oneBandRaster));
+ assertEquals(4326, Functions.srid(multiBandRaster));
+ }
+
@Test
public void value() throws TransformException {
assertNull("Points outside of the envelope should return null.", Functions.value(oneBandRaster, point(1, 1), 1));
diff --git a/docs/api/sql/Raster-operators.md b/docs/api/sql/Raster-operators.md
index 1011c303..0388d35a 100644
--- a/docs/api/sql/Raster-operators.md
+++ b/docs/api/sql/Raster-operators.md
@@ -35,6 +35,24 @@ Output:
4
```
+### RS_SRID
+
+Introduction: Returns the spatial reference system identifier (SRID) of the raster geometry.
+
+Format: `RS_SRID (raster: Raster)`
+
+Since: `v1.4.1`
+
+Spark SQL example:
+```sql
+SELECT RS_SRID(raster) FROM raster_table
+```
+
+Output:
+```
+3857
+```
+
### RS_Value
Introduction: Returns the value at the given point in the raster.
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 3af49b67..109d095d 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
@@ -173,6 +173,7 @@ object Catalog {
function[RS_FromGeoTiff](),
function[RS_Envelope](),
function[RS_NumBands](),
+ function[RS_SRID](),
function[RS_Value](1),
function[RS_Values](1)
)
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/Functions.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/Functions.scala
index af2edcdf..62c43a3f 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/Functions.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/Functions.scala
@@ -855,6 +855,29 @@ case class RS_NumBands(inputExpressions: Seq[Expression]) extends Expression wit
override def inputTypes: Seq[AbstractDataType] = Seq(RasterUDT)
}
+case class RS_SRID(inputExpressions: Seq[Expression]) extends Expression with CodegenFallback with ExpectsInputTypes {
+ override def nullable: Boolean = true
+
+ override def eval(input: InternalRow): Any = {
+ val raster = inputExpressions(0).toRaster(input)
+ if (raster == null) {
+ null
+ } else {
+ Functions.srid(raster)
+ }
+ }
+
+ override def dataType: DataType = IntegerType
+
+ override def children: Seq[Expression] = inputExpressions
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
+ copy(inputExpressions = newChildren)
+ }
+
+ override def inputTypes: Seq[AbstractDataType] = Seq(RasterUDT)
+}
+
case class RS_Value(inputExpressions: Seq[Expression]) extends Expression with CodegenFallback with ExpectsInputTypes {
override def nullable: Boolean = true
diff --git a/sql/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala b/sql/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
index 1bde6913..09ec4d44 100644
--- a/sql/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
+++ b/sql/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
@@ -290,6 +290,17 @@ class rasteralgebraTest extends TestBaseScala with BeforeAndAfter with GivenWhen
assert(result == 1)
}
+ it("Passed RS_SRID should handle null values") {
+ val result = sparkSession.sql("select RS_SRID(null)").first().get(0)
+ assert(result == null)
+ }
+
+ it("Passed RS_SRID with raster") {
+ val df = sparkSession.read.format("binaryFile").load(resourceFolder + "raster/test1.tiff")
+ val result = df.selectExpr("RS_SRID(RS_FromGeoTiff(content))").first().getInt(0)
+ assert(result == 3857)
+ }
+
it("Passed RS_Value should handle null values") {
val result = sparkSession.sql("select RS_Value(null, null)").first().get(0)
assert(result == null)