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))
+ }
}
}