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") {