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 40719991a [SEDONA-653] Add a lenient mode for RS_Clip and make it 
lenient by default (#1586)
40719991a is described below

commit 40719991a4e9c97ae93af16c9aa4188c5aaf8cfe
Author: Kristin Cowalcijk <[email protected]>
AuthorDate: Sat Sep 14 07:11:59 2024 +0800

    [SEDONA-653] Add a lenient mode for RS_Clip and make it lenient by default 
(#1586)
---
 .../sedona/common/raster/RasterBandEditors.java    | 37 ++++++++++++++++++++--
 .../common/raster/RasterBandEditorsTest.java       | 24 ++++++++++++++
 docs/api/sql/Raster-operators.md                   |  7 +++-
 .../expressions/raster/RasterBandEditors.scala     |  1 +
 .../org/apache/sedona/sql/rasteralgebraTest.scala  |  7 ++++
 5 files changed, 73 insertions(+), 3 deletions(-)

diff --git 
a/common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java 
b/common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java
index 852a81db4..781ef7bed 100644
--- 
a/common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java
+++ 
b/common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.sedona.common.utils.RasterUtils;
 import org.geotools.coverage.GridSampleDimension;
 import org.geotools.coverage.grid.GridCoverage2D;
+import org.geotools.coverage.processing.CannotCropException;
 import org.geotools.coverage.processing.operation.Crop;
 import org.locationtech.jts.geom.Geometry;
 import org.opengis.parameter.ParameterValueGroup;
@@ -273,10 +274,17 @@ public class RasterBandEditors {
    * @param geometry Specify ROI
    * @param noDataValue no-Data value for empty cells
    * @param crop Specifies to keep the original extent or not
+   * @param lenient Return null if the raster and geometry do not intersect 
when set to true,
+   *     otherwise will throw an exception
    * @return A clip Raster with defined ROI by the geometry
    */
   public static GridCoverage2D clip(
-      GridCoverage2D raster, int band, Geometry geometry, double noDataValue, 
boolean crop)
+      GridCoverage2D raster,
+      int band,
+      Geometry geometry,
+      double noDataValue,
+      boolean crop,
+      boolean lenient)
       throws FactoryException, TransformException {
 
     // Selecting the band from original raster
@@ -296,7 +304,16 @@ public class RasterBandEditors {
     parameters.parameter(Crop.PARAMNAME_DEST_NODATA).setValue(new double[] 
{noDataValue});
     parameters.parameter(Crop.PARAMNAME_ROI).setValue(geometry);
 
-    GridCoverage2D newRaster = (GridCoverage2D) 
cropObject.doOperation(parameters, null);
+    GridCoverage2D newRaster;
+    try {
+      newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
+    } catch (CannotCropException e) {
+      if (lenient) {
+        return null;
+      } else {
+        throw e;
+      }
+    }
 
     if (!crop) {
       double[] metadataOriginal = RasterAccessors.metadata(raster);
@@ -383,6 +400,22 @@ public class RasterBandEditors {
     return newRaster;
   }
 
+  /**
+   * Return a clipped raster with the specified ROI by the geometry
+   *
+   * @param raster Raster to clip
+   * @param band Band number to perform clipping
+   * @param geometry Specify ROI
+   * @param noDataValue no-Data value for empty cells
+   * @param crop Specifies to keep the original extent or not
+   * @return A clip Raster with defined ROI by the geometry
+   */
+  public static GridCoverage2D clip(
+      GridCoverage2D raster, int band, Geometry geometry, double noDataValue, 
boolean crop)
+      throws FactoryException, TransformException {
+    return clip(raster, band, geometry, noDataValue, crop, true);
+  }
+
   /**
    * Return a clipped raster with the specified ROI by the geometry.
    *
diff --git 
a/common/src/test/java/org/apache/sedona/common/raster/RasterBandEditorsTest.java
 
b/common/src/test/java/org/apache/sedona/common/raster/RasterBandEditorsTest.java
index f23aa279d..6a560b728 100644
--- 
a/common/src/test/java/org/apache/sedona/common/raster/RasterBandEditorsTest.java
+++ 
b/common/src/test/java/org/apache/sedona/common/raster/RasterBandEditorsTest.java
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
 import org.apache.sedona.common.Constructors;
 import org.apache.sedona.common.raster.serde.Serde;
 import org.geotools.coverage.grid.GridCoverage2D;
+import org.geotools.coverage.processing.CannotCropException;
 import org.junit.Test;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.io.ParseException;
@@ -247,6 +248,29 @@ public class RasterBandEditorsTest extends RasterTestBase {
     assertTrue(Arrays.equals(expectedValues, actualValues));
   }
 
+  @Test
+  public void testClipLenient()
+      throws FactoryException, IOException, ParseException, TransformException 
{
+    GridCoverage2D raster =
+        rasterFromGeoTiff(resourceFolder + 
"raster_geotiff_color/FAA_UTM18N_NAD83.tif");
+
+    // Construct a polygon that does not intersect with the raster
+    Geometry nonIntersectingGeom =
+        Constructors.geomFromWKT(
+            "POLYGON ((-78.22106647832458748 37.76411511479908967, 
-78.20183062098976734 37.72863564460374874, -78.18088490966962922 
37.76753482276972562, -78.22106647832458748 37.76411511479908967))",
+            0);
+
+    // Throws an exception in non-lenient mode
+    assertThrows(
+        CannotCropException.class,
+        () -> RasterBandEditors.clip(raster, 1, nonIntersectingGeom, 200, 
false, false));
+
+    // Returns null in lenient mode
+    GridCoverage2D result = RasterBandEditors.clip(raster, 1, 
nonIntersectingGeom, 200, false);
+    assertNull(result);
+    raster.dispose(true);
+  }
+
   @Test
   public void testRasterUnion() throws FactoryException {
     double[][] rasterData1 =
diff --git a/docs/api/sql/Raster-operators.md b/docs/api/sql/Raster-operators.md
index 91c72ee25..f1c0f757f 100644
--- a/docs/api/sql/Raster-operators.md
+++ b/docs/api/sql/Raster-operators.md
@@ -1428,10 +1428,15 @@ Introduction: Returns a raster that is clipped by the 
given geometry.
 If `crop` is not specified then it will default to `true`, meaning it will 
make the resulting raster shrink to the geometry's extent and if `noDataValue` 
is not specified then the resulting raster will have the minimum possible value 
for the band pixel data type.
 
 !!!Note
-    Since `v1.5.1`, if the coordinate reference system (CRS) of the input 
`geom` geometry differs from that of the `raster`, then `geom` will be 
transformed to match the CRS of the `raster`. If the `raster` or `geom` doesn't 
have a CRS then it will default to `4326/WGS84`.
+    - Since `v1.5.1`, if the coordinate reference system (CRS) of the input 
`geom` geometry differs from that of the `raster`, then `geom` will be 
transformed to match the CRS of the `raster`. If the `raster` or `geom` doesn't 
have a CRS then it will default to `4326/WGS84`.
+    - Since `v1.7.0`, `RS_Clip` function will return `null` if the `raster` 
and `geometry` geometry do not intersect. If you want to throw an exception in 
this case, you can set the `lenient` parameter to `false`.
 
 Format:
 
+```
+RS_Clip(raster: Raster, band: Integer, geom: Geometry, noDataValue: Double, 
crop: Boolean, lenient: Boolean)
+```
+
 ```
 RS_Clip(raster: Raster, band: Integer, geom: Geometry, noDataValue: Double, 
crop: Boolean)
 ```
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterBandEditors.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterBandEditors.scala
index 2026144b1..b9690115f 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterBandEditors.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/raster/RasterBandEditors.scala
@@ -59,6 +59,7 @@ case class RS_Union(inputExpressions: Seq[Expression])
 
 case class RS_Clip(inputExpressions: Seq[Expression])
     extends InferredExpression(
+      inferrableFunction6(RasterBandEditors.clip),
       inferrableFunction5(RasterBandEditors.clip),
       inferrableFunction4(RasterBandEditors.clip),
       inferrableFunction3(RasterBandEditors.clip)) {
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
index ea81708ae..b63953294 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
@@ -1027,6 +1027,13 @@ class rasteralgebraTest extends TestBaseScala with 
BeforeAndAfter with GivenWhen
       expectedValues = Seq(0.0, 0.0, 0.0, 0.0, null)
       assertTrue(expectedValues.equals(actualValues))
 
+      // Test with a polygon that does not intersect the raster in lenient mode
+      val actual = df
+        .selectExpr(
+          "RS_Clip(raster, 1, ST_GeomFromWKT('POLYGON((274157 4174899,263510 
4174947,269859 4183348,274157 4174899))'))")
+        .first()
+        .get(0)
+      assertNull(actual)
     }
 
     it("Passed RS_AsGeoTiff") {

Reply via email to