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 a05872722b5..3abd56869eb 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 @@ -31,7 +31,10 @@ import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.processing.CannotCropException; import org.geotools.coverage.processing.operation.Crop; +import org.geotools.geometry.Envelope2D; +import org.geotools.geometry.jts.JTS; import org.locationtech.jts.geom.Geometry; +import org.opengis.geometry.BoundingBox; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.FactoryException; import org.opengis.referencing.operation.TransformException; @@ -298,13 +301,19 @@ public static GridCoverage2D clip( singleBandRaster = pair.getLeft(); geometry = pair.getRight(); + // Use rasterizeGeomExtent for AOI geometries smaller than a pixel + double[] metadata = RasterAccessors.metadata(singleBandRaster); + Envelope2D geomEnvelope = + Rasterization.rasterizeGeomExtent(geometry, singleBandRaster, metadata, allTouched); + Geometry geomExtent = JTS.toGeometry((BoundingBox) geomEnvelope); + // Crop the raster // this will shrink the extent of the raster to the geometry Crop cropObject = new Crop(); ParameterValueGroup parameters = cropObject.getParameters(); parameters.parameter("Source").setValue(singleBandRaster); parameters.parameter(Crop.PARAMNAME_DEST_NODATA).setValue(new double[] {noDataValue}); - parameters.parameter(Crop.PARAMNAME_ROI).setValue(geometry); + parameters.parameter(Crop.PARAMNAME_ROI).setValue(geomExtent); GridCoverage2D newRaster; try { diff --git a/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java b/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java index 19530441bb8..5d15990d6fe 100644 --- a/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java +++ b/common/src/main/java/org/apache/sedona/common/raster/Rasterization.java @@ -303,7 +303,7 @@ private static boolean isInsideBounds( return x >= minX && x <= maxX && y >= minY && y <= maxY; } - private static Envelope2D rasterizeGeomExtent( + protected static Envelope2D rasterizeGeomExtent( Geometry geom, GridCoverage2D raster, double[] metadata, boolean allTouched) { if (Objects.equals(geom.getGeometryType(), "MultiLineString")) { 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 73f81e8ed1b..45de7c7e305 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 @@ -201,6 +201,28 @@ public void testClipWithGeometryTransform() assertTrue(Arrays.equals(expectedValues, actualValues)); } + @Test + public void testClip_smallAOI() throws FactoryException, TransformException, ParseException { + // Test for AOI geometries smaller than a pixel + GridCoverage2D raster = + RasterConstructors.makeEmptyRaster(1, "F", 4, 4, 401805.039562261, 2095852.150947876, 30); + raster = RasterEditors.setSrid(raster, 5070); + double[] bandValues = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + raster = MapAlgebra.addBandFromArray(raster, bandValues, 1); + String polygon = + "POLYGON ((401866.1071465613 2095803.8989834636, 401850.9983055725 2095803.1375789386, 401850.039562261 2095822.150947876, 401865.14840359957 2095822.9124532426, 401880.2572475523 2095823.6738547424, 401881.2159852499 2095804.6604855175, 401866.1071465613 2095803.8989834636))"; + Geometry geom = Constructors.geomFromWKT(polygon, 5070); + + GridCoverage2D clippedRaster = RasterBandEditors.clip(raster, 1, geom, false, 0, true); + double bandNoDataValue = RasterBandAccessors.getBandNoDataValue(clippedRaster); + double expectedBandNoDataValue = 0.0; + double[] actualValues = MapAlgebra.bandAsArray(clippedRaster, 1); + double[] expectedValues = {2.0, 3.0, 6.0, 7.0}; + + assertEquals(expectedBandNoDataValue, bandNoDataValue, FP_TOLERANCE); + assertTrue(Arrays.equals(expectedValues, actualValues)); + } + @Test public void testClip() throws IOException, FactoryException, TransformException, ParseException, @@ -244,7 +266,7 @@ public void testClip() points.add(Constructors.geomFromWKT("POINT(237919 4.20357e+06)", 26918)); points.add(Constructors.geomFromWKT("POINT(223802 4.20465e+06)", 26918)); actualValues = PixelFunctions.values(croppedRaster, points, 1).toArray(new Double[0]); - expectedValues = new Double[] {0.0, 0.0, 0.0, 0.0, null}; + expectedValues = new Double[] {0.0, 0.0, 0.0, 0.0, 255.0}; assertTrue(Arrays.equals(expectedValues, actualValues)); }