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 4adf36c65e..68feb92d20 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 @@ -32,6 +32,7 @@ import javax.media.jai.RasterFactory; import java.awt.geom.Point2D; import java.awt.image.Raster; +import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Arrays; import java.util.Collections; @@ -259,7 +260,14 @@ public static GridCoverage2D clip(GridCoverage2D raster, int band, Geometry geom } newRaster = RasterUtils.clone(resultRaster, raster.getGridGeometry(), newRaster.getSampleDimensions(), newRaster, noDataValue, true); } else { - newRaster = RasterUtils.clone(newRaster.getRenderedImage(), newRaster.getGridGeometry(), newRaster.getSampleDimensions(), newRaster, noDataValue, true); + RenderedImage image = newRaster.getRenderedImage(); + int minX = image.getMinX(); + int minY = image.getMinY(); + if (minX != 0 || minY != 0) { + newRaster = RasterUtils.shiftRasterToZeroOrigin(newRaster, noDataValue); + } else { + newRaster = RasterUtils.clone(newRaster.getRenderedImage(), newRaster.getGridGeometry(), newRaster.getSampleDimensions(), newRaster, noDataValue, true); + } } return newRaster; @@ -295,24 +303,4 @@ public static GridCoverage2D clip(GridCoverage2D raster, int band, Geometry geom return clip(raster, band, geometry, noDataValue, true); } } - -// /** -// * 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 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, boolean crop) { -// boolean isDataTypeIntegral = RasterUtils.isDataTypeIntegral(RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, band))); -// -// if (isDataTypeIntegral) { -// double noDataValue = Integer.MIN_VALUE; -// return clip(raster, band, geometry, noDataValue, crop); -// } else { -// double noDataValue = Double.MIN_VALUE; -// return clip(raster, band, geometry, noDataValue, crop); -// } -// } } diff --git a/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java b/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java index 5bf18f84a9..3b694b91d2 100644 --- a/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java +++ b/common/src/main/java/org/apache/sedona/common/utils/RasterUtils.java @@ -31,6 +31,7 @@ import org.geotools.coverage.grid.GridCoordinates2D; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; +import org.geotools.coverage.grid.GridEnvelope2D; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.DirectPosition2D; import org.geotools.referencing.crs.DefaultEngineeringCRS; @@ -38,6 +39,7 @@ import org.geotools.util.ClassChanger; import org.geotools.util.NumberRange; import org.locationtech.jts.geom.Geometry; +import org.opengis.coverage.grid.GridEnvelope; import org.opengis.geometry.DirectPosition; import org.opengis.metadata.spatial.PixelOrientation; import org.opengis.referencing.FactoryException; @@ -59,6 +61,7 @@ import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.*; @@ -642,4 +645,32 @@ public static void isRasterSameShape(GridCoverage2D raster1, GridCoverage2D rast "Second raster having width of %d and height of %d", width1, height1, width2, height2)); } } + + /** + * Shift the rendered image inside the grid coverage to have a zero origin. This won't alter the cell values of + * the raster, but will shift the affine transformation to cancel with the origin shift. + * @param raster The raster to shift + * @param noDataValue The no data value to use for the new raster + * @return A new grid coverage with the same cell values but its rendered image has a zero origin + */ + public static GridCoverage2D shiftRasterToZeroOrigin(GridCoverage2D raster, Double noDataValue) { + RenderedImage image = raster.getRenderedImage(); + SampleModel sampleModel = image.getSampleModel(); + int width = image.getWidth(); + int height = image.getHeight(); + int minX = image.getMinX(); + int minY = image.getMinY(); + if (minX != 0 || minY != 0) { + GridGeometry2D gridGeometry = raster.getGridGeometry(); + AffineTransform2D transform = (AffineTransform2D) gridGeometry.getGridToCRS2D(); + AffineTransform2D newAffine = RasterUtils.translateAffineTransform(transform, minX, minY); + GridEnvelope newGridEnvelope = new GridEnvelope2D(0, 0, width, height); + GridGeometry2D newGridGeometry = new GridGeometry2D(newGridEnvelope, newAffine, gridGeometry.getCoordinateReferenceSystem()); + WritableRaster wr = RasterFactory.createBandedRaster(sampleModel.getDataType(), image.getWidth(), image.getHeight(), sampleModel.getNumBands(), null); + wr.setRect(-minX, -minY, RasterUtils.getRaster(image)); + return RasterUtils.clone(wr, newGridGeometry, raster.getSampleDimensions(), raster, noDataValue, true); + } else { + return RasterUtils.clone(raster.getRenderedImage(), raster.getGridGeometry(), raster.getSampleDimensions(), raster, noDataValue, true); + } + } } 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 e1a8715f13..9754d5b5e1 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 @@ -189,6 +189,8 @@ public void testClip() throws IOException, FactoryException, TransformException, assertTrue(Arrays.equals(expectedValues, actualValues)); GridCoverage2D croppedRaster = RasterBandEditors.clip(raster, 1, geom, 200, true); + assertEquals(0, croppedRaster.getRenderedImage().getMinX()); + assertEquals(0, croppedRaster.getRenderedImage().getMinY()); points = new ArrayList<>(); points.add(Constructors.geomFromWKT("POINT(236842 4.20465e+06)", 26918)); points.add(Constructors.geomFromWKT("POINT(236961 4.20453e+06)", 26918));