Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SEDONA-653] Add a lenient mode for RS_Clip and make it lenient by default #1586

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading