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 2a5fe3e0 [SEDONA-343] Add RS_Contains and RS_Within (#946)
2a5fe3e0 is described below

commit 2a5fe3e0080a13cf6b32d5f73d5688675981112d
Author: Nilesh Gajwani <[email protected]>
AuthorDate: Sat Aug 5 13:45:19 2023 -0700

    [SEDONA-343] Add RS_Contains and RS_Within (#946)
---
 .../sedona/common/raster/RasterPredicates.java     |  37 +++-
 .../sedona/common/raster/RasterPredicatesTest.java | 194 ++++++++++++++++++++-
 .../sedona/common/raster/RasterTestBase.java       |   1 -
 docs/api/sql/Raster-operators.md                   |  40 +++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |   4 +-
 .../expressions/raster/RasterPredicates.scala      |  12 ++
 .../org/apache/sedona/sql/rasteralgebraTest.scala  |  10 ++
 7 files changed, 288 insertions(+), 10 deletions(-)

diff --git 
a/common/src/main/java/org/apache/sedona/common/raster/RasterPredicates.java 
b/common/src/main/java/org/apache/sedona/common/raster/RasterPredicates.java
index ec492df9..5aa6c06d 100644
--- a/common/src/main/java/org/apache/sedona/common/raster/RasterPredicates.java
+++ b/common/src/main/java/org/apache/sedona/common/raster/RasterPredicates.java
@@ -46,20 +46,43 @@ public class RasterPredicates {
     public static boolean rsIntersects(GridCoverage2D raster, Geometry 
queryWindow) {
         Envelope2D rasterEnvelope2D = raster.getEnvelope2D();
         CoordinateReferenceSystem rasterCRS = 
rasterEnvelope2D.getCoordinateReferenceSystem();
-        int queryWindowSRID = queryWindow.getSRID();
-        if (rasterCRS != null && !(rasterCRS instanceof DefaultEngineeringCRS) 
&& queryWindowSRID > 0) {
+        queryWindow = convertCRSIfNeeded(queryWindow, rasterCRS);
+        Envelope rasterEnvelope = JTS.toEnvelope(rasterEnvelope2D);
+        Geometry rasterGeometry = GEOMETRY_FACTORY.toGeometry(rasterEnvelope);
+        return rasterGeometry.intersects(queryWindow);
+    }
+
+    public static boolean rsContains(GridCoverage2D raster, Geometry geometry) 
{
+        Envelope2D rasterEnvelope2D = raster.getEnvelope2D();
+        CoordinateReferenceSystem rasterCRS = 
rasterEnvelope2D.getCoordinateReferenceSystem();
+        geometry = convertCRSIfNeeded(geometry, rasterCRS);
+        Envelope rasterEnvelope = JTS.toEnvelope(rasterEnvelope2D);
+        Geometry rasterGeometry = GEOMETRY_FACTORY.toGeometry(rasterEnvelope);
+        return  rasterGeometry.contains(geometry);
+    }
+
+    public static boolean rsWithin(GridCoverage2D raster, Geometry geometry) {
+        Envelope2D rasterEnvelope2D = raster.getEnvelope2D();
+        CoordinateReferenceSystem rasterCRS = 
rasterEnvelope2D.getCoordinateReferenceSystem();
+        geometry = convertCRSIfNeeded(geometry, rasterCRS);
+        Envelope rasterEnvelope = JTS.toEnvelope(rasterEnvelope2D);
+        Geometry rasterGeometry = GEOMETRY_FACTORY.toGeometry(rasterEnvelope);
+        return  rasterGeometry.within(geometry);
+    }
+
+    private static Geometry convertCRSIfNeeded(Geometry geometry, 
CoordinateReferenceSystem rasterCRS) {
+        int geomSRID = geometry.getSRID();
+        if (rasterCRS != null && !(rasterCRS instanceof DefaultEngineeringCRS) 
&& geomSRID > 0) {
             try {
-                CoordinateReferenceSystem queryWindowCRS = CRS.decode("EPSG:" 
+ queryWindowSRID);
+                CoordinateReferenceSystem queryWindowCRS = CRS.decode("EPSG:" 
+ geomSRID);
                 if (!CRS.equalsIgnoreMetadata(rasterCRS, queryWindowCRS)) {
                     MathTransform transform = 
CRS.findMathTransform(queryWindowCRS, rasterCRS, true);
-                    queryWindow = JTS.transform(queryWindow, transform);
+                    geometry = JTS.transform(geometry, transform);
                 }
             } catch (FactoryException | TransformException e) {
                 throw new RuntimeException("Cannot transform CRS of query 
window", e);
             }
         }
-        Envelope rasterEnvelope = JTS.toEnvelope(rasterEnvelope2D);
-        Geometry rasterGeometry = GEOMETRY_FACTORY.toGeometry(rasterEnvelope);
-        return rasterGeometry.intersects(queryWindow);
+        return geometry;
     }
 }
diff --git 
a/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
 
b/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
index 2c18059e..22ecde40 100644
--- 
a/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/raster/RasterPredicatesTest.java
@@ -19,13 +19,21 @@
 package org.apache.sedona.common.raster;
 
 import org.geotools.coverage.grid.GridCoverage2D;
+import org.geotools.geometry.jts.JTS;
+import org.geotools.referencing.CRS;
 import org.junit.Assert;
 import org.junit.Test;
 import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.PrecisionModel;
+import org.locationtech.jts.io.ParseException;
+import org.opengis.referencing.FactoryException;
+import org.opengis.referencing.operation.TransformException;
 
 import java.awt.image.DataBuffer;
+import java.io.IOException;
+
 
 public class RasterPredicatesTest extends RasterTestBase {
     private static final GeometryFactory GEOMETRY_FACTORY = new 
GeometryFactory();
@@ -43,7 +51,7 @@ public class RasterPredicatesTest extends RasterTestBase {
     @Test
     public void testIntersectsQueryWindowNoCrs() {
         Geometry queryWindow = GEOMETRY_FACTORY.toGeometry(new Envelope(0, 10, 
0, 10));
-        GridCoverage2D raster = createRandomRaster(DataBuffer.TYPE_BYTE, 100, 
100, 0, 100, 1, 1, "EPSG:3857");
+        GridCoverage2D raster = createRandomRaster(DataBuffer.TYPE_BYTE, 100, 
100, 0, 100, 1, 1, "EPSG:4326");
         boolean result = RasterPredicates.rsIntersects(raster, queryWindow);
         Assert.assertTrue(result);
         queryWindow = GEOMETRY_FACTORY.toGeometry(new Envelope(1000, 1010, 
1000, 1010));
@@ -93,4 +101,188 @@ public class RasterPredicatesTest extends RasterTestBase {
         result = RasterPredicates.rsIntersects(raster, queryWindow);
         Assert.assertFalse(result);
     }
+
+    @Test
+    public void testContainsNoCrs() throws FactoryException {
+        Geometry geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(5, 10, 5, 
10));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1);
+        boolean result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(2, 22, 2, 22));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //geometry protruding out of the raster envelope
+        geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(2, 20, 2, 25));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testContainsGeomNoCrs() throws FactoryException {
+        Geometry geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(5, 10, 5, 
10));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1, -1, 0, 0, 4326);
+        boolean result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(2, 22, 2, 22));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //geometry protruding out of the raster envelope
+        geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(2, 20, 2, 25));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testContainsRasterNoCrs() throws FactoryException, 
ParseException, IOException {
+        GeometryFactory geometryFactory = new GeometryFactory(new 
PrecisionModel(), 4326);
+        Geometry geometry = geometryFactory.toGeometry(new Envelope(5, 10, 5, 
10));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1);
+        boolean result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        geometry = geometryFactory.toGeometry(new Envelope(2, 22, 2, 22));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //geometry protruding out of the raster envelope
+        geometry = geometryFactory.toGeometry(new Envelope(2, 20, 2, 25));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testContainsSameCrs() throws FactoryException {
+        GeometryFactory geometryFactory = new GeometryFactory(new 
PrecisionModel(), 4326);
+        Geometry geometry = geometryFactory.toGeometry(new Envelope(5, 10, 5, 
10));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1, -1, 0, 0, 4326);
+        boolean result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        geometry = geometryFactory.toGeometry(new Envelope(2, 22, 2, 22));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //geometry protruding out of the raster envelope
+        geometry = geometryFactory.toGeometry(new Envelope(2, 20, 2, 25));
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testContainsDifferentCrs() throws FactoryException, 
TransformException {
+        GeometryFactory geometryFactory = new GeometryFactory(new 
PrecisionModel(), 3857);
+        Geometry geometry = geometryFactory.toGeometry(new Envelope(5, 10, 5, 
10));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1, -1, 0, 0, 3857);
+        geometry = JTS.transform(geometry, 
CRS.findMathTransform(raster.getCoordinateReferenceSystem(), 
CRS.decode("EPSG:4326")));
+        geometry.setSRID(4326);
+        boolean result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertTrue(result);
+
+        //geometry protruding out of the raster envelope
+        geometry = geometryFactory.toGeometry(new Envelope(2, 20, 2, 25));
+        geometry = JTS.transform(geometry, 
CRS.findMathTransform(raster.getCoordinateReferenceSystem(), 
CRS.decode("EPSG:4326")));
+        geometry.setSRID(4326);
+        result = RasterPredicates.rsContains(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+
+    @Test
+    public void testWithinNoCrs() throws FactoryException {
+        Geometry geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(0, 100, 
0, 50));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1);
+        boolean result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 50, 0, 50, 1);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //raster protruding out of the geometry
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 100, 0, 50, 1);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testWithinGeomNoCrs() throws FactoryException {
+        Geometry geometry = GEOMETRY_FACTORY.toGeometry(new Envelope(0, 100, 
0, 50));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1, -1, 0, 0, 3857);
+        boolean result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 50, 0, 50, 1, -1, 
0, 0, 3857);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //raster protruding out of the geometry
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 100, 0, 50, 1, -1, 
0, 0, 3857);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testWithinRasterNoCrs() throws FactoryException {
+        GeometryFactory geometryFactory = new GeometryFactory(new 
PrecisionModel(), 4326);
+        Geometry geometry = geometryFactory.toGeometry(new Envelope(0, 100, 0, 
50));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1);
+        boolean result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 50, 0, 50, 1);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //raster protruding out of the geometry
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 100, 0, 50, 1);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testWithinSameCrs() throws FactoryException {
+        GeometryFactory geometryFactory = new GeometryFactory(new 
PrecisionModel(), 4326);
+        Geometry geometry = geometryFactory.toGeometry(new Envelope(0, 100, 0, 
50));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
2, 22, 1, -1, 0, 0, 4326);
+        boolean result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //overlapping raster and geometry;
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 50, 0, 50, 1, -1, 
0, 0, 4326);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+        //raster protruding out of the geometry
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 100, 0, 50, 1, -1, 
0, 0, 4326);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertFalse(result);
+    }
+
+    @Test
+    public void testWithinDifferentCrs() throws FactoryException, 
TransformException {
+        GeometryFactory geometryFactory = new GeometryFactory(new 
PrecisionModel(), 4326);
+        Geometry geometry = geometryFactory.toGeometry(new Envelope(30, 60, 
10, 50));
+        GridCoverage2D raster = RasterConstructors.makeEmptyRaster(1, 20, 20, 
32, 35, 1, -1, 0, 0, 4326);
+        geometry = JTS.transform(geometry, 
CRS.findMathTransform(raster.getCoordinateReferenceSystem(), 
CRS.decode("EPSG:3857")));
+        geometry.setSRID(3857);
+        boolean result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertTrue(result);
+
+
+        //raster protruding out of the geometry
+        raster = RasterConstructors.makeEmptyRaster(1, 100, 100, 0, 50, 1, -1, 
0, 0, 4326);
+        result = RasterPredicates.rsWithin(raster, geometry);
+        Assert.assertFalse(result);
+    }
 }
diff --git 
a/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java 
b/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java
index 4bd37374..a8a49dbd 100644
--- a/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java
+++ b/common/src/test/java/org/apache/sedona/common/raster/RasterTestBase.java
@@ -15,7 +15,6 @@ package org.apache.sedona.common.raster;
 
 import org.geotools.coverage.grid.GridCoverage2D;
 import org.geotools.coverage.grid.GridCoverageFactory;
-import org.geotools.data.DataSourceException;
 import org.geotools.gce.geotiff.GeoTiffReader;
 import org.geotools.gce.geotiff.GeoTiffWriter;
 import org.geotools.geometry.Envelope2D;
diff --git a/docs/api/sql/Raster-operators.md b/docs/api/sql/Raster-operators.md
index eae7ae12..0e8617a9 100644
--- a/docs/api/sql/Raster-operators.md
+++ b/docs/api/sql/Raster-operators.md
@@ -215,6 +215,46 @@ Output:
 true
 ```
 
+### RS_Within
+
+Introduction: Returns true if the envelope of the raster is within the given 
geometry. If the geometry does not have a
+defined SRID, it is considered to be in the same CRS with the raster. If the 
geometry has a defined SRID, the geometry
+will be transformed to the CRS of the raster before checking the within 
relationship.
+
+Format: `RS_Within(raster: Raster, geom: Geometry)`
+
+Since: `1.5.0`
+
+Spark SQL example:
+```sql
+SELECT RS_Within(RS_MakeEmptyRaster(1, 20, 20, 2, 22, 1), 
ST_GeomFromWKT('POLYGON ((0 0, 0 50, 100 50, 100 0, 0 0))'));
+```
+
+Output:
+```
+true
+```
+
+### RS_Contains
+
+Introduction: Returns true if the envelope of the raster contains the given 
geometry. If the geometry does not have a
+defined SRID, it is considered to be in the same CRS with the raster. If the 
geometry has a defined SRID, the geometry
+will be transformed to the CRS of the raster before checking the contains 
relationship.
+
+Format: `RS_Contains(raster: Raster, geom: Geometry)`
+
+Since: `1.5.0`
+
+Spark SQL example:
+```sql
+SELECT RS_Contains(RS_MakeEmptyRaster(1, 20, 20, 2, 22, 1), 
ST_GeomFromWKT('POLYGON ((5 5, 5 10, 10 10, 10 5, 5 5))'));
+```
+
+Output:
+```
+true
+```
+
 ### RS_MetaData
 
 Introduction: Returns the metadata of the raster as an array of double. The 
array contains the following values:
diff --git a/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala 
b/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index 755ba799..f10883ea 100644
--- a/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -214,7 +214,9 @@ object Catalog {
     function[RS_ScaleX](),
     function[RS_ScaleY](),
     function[RS_PixelAsPoint](),
-    function[RS_ConvexHull]()
+    function[RS_ConvexHull](),
+    function[RS_Within](),
+    function[RS_Contains]()
   )
 
   val aggregateExpressions: Seq[Aggregator[Geometry, Geometry, Geometry]] = 
Seq(
diff --git 
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterPredicates.scala
 
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterPredicates.scala
index 54cfd6d7..95d746d8 100644
--- 
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterPredicates.scala
+++ 
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterPredicates.scala
@@ -28,3 +28,15 @@ case class RS_Intersects(inputExpressions: Seq[Expression]) 
extends InferredExpr
     copy(inputExpressions = newChildren)
   }
 }
+
+case class RS_Within(inputExpressions: Seq[Expression]) extends 
InferredExpression(RasterPredicates.rsWithin _) {
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
+case class RS_Contains(inputExpressions: Seq[Expression]) extends 
InferredExpression(RasterPredicates.rsContains _) {
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
diff --git 
a/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala 
b/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
index 7dd4e16e..3b94916f 100644
--- a/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
+++ b/sql/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
@@ -558,5 +558,15 @@ class rasteralgebraTest extends TestBaseScala with 
BeforeAndAfter with GivenWhen
       assertEquals(expectedX, actualCoordinates.x, 1e-5)
       assertEquals(expectedY, actualCoordinates.y, 1e-5)
     }
+
+    it("Passed RS_Contains") {
+      assert(sparkSession.sql("SELECT RS_Contains(RS_MakeEmptyRaster(1, 20, 
20, 2, 22, 1), ST_GeomFromWKT('POLYGON ((5 5, 5 10, 10 10, 10 5, 5 
5))'))").first().getBoolean(0))
+      assert(!sparkSession.sql("SELECT RS_Contains(RS_MakeEmptyRaster(1, 20, 
20, 2, 22, 1), ST_GeomFromWKT('POLYGON ((2 2, 2 25, 20 25, 20 2, 2 
2))'))").first().getBoolean(0))
+    }
+
+    it("Passed RS_Within") {
+      assert(sparkSession.sql("SELECT RS_WITHIN(RS_MakeEmptyRaster(1, 20, 20, 
2, 22, 1), ST_GeomFromWKT('POLYGON ((0 0, 0 50, 100 50, 100 0, 0 
0))'))").first().getBoolean(0))
+      assert(!sparkSession.sql("SELECT RS_WITHIN(RS_MakeEmptyRaster(1, 100, 
100, 0, 50, 1), ST_GeomFromWKT('POLYGON ((2 2, 2 25, 20 25, 20 2, 2 
2))'))").first().getBoolean(0))
+    }
   }
 }

Reply via email to