Skip to content

Commit

Permalink
[SEDONA-653] Add a lenient mode for RS_Clip and make it lenient by de…
Browse files Browse the repository at this point in the history
…fault (#1586)
  • Loading branch information
Kontinuation authored Sep 13, 2024
1 parent 20488c0 commit 4071999
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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;
Expand Down Expand Up @@ -273,10 +274,17 @@ private static void ensureBandAppend(GridCoverage2D raster, int band) {
* @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
Expand All @@ -296,7 +304,16 @@ public static GridCoverage2D clip(
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);
Expand Down Expand Up @@ -383,6 +400,22 @@ public static GridCoverage2D clip(
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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -247,6 +248,29 @@ public void testClip()
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 =
Expand Down
7 changes: 6 additions & 1 deletion docs/api/sql/Raster-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down

0 comments on commit 4071999

Please sign in to comment.