Copilot commented on code in PR #2400:
URL: https://github.com/apache/sedona/pull/2400#discussion_r2430575656


##########
common/src/main/java/org/apache/sedona/common/raster/Rasterization.java:
##########
@@ -574,17 +626,18 @@ private static Map<Double, TreeSet<Double>> 
computeScanlineIntersections(
           // calculating slope
           for (double y = yStart; y >= yEnd; y--) {
             double xIntercept = p1X; // Vertical line, xIntercept is constant
-            if (xIntercept < 0 || xIntercept >= 
params.writableRaster.getWidth()) {
+            if (xIntercept < 0 || xIntercept > 
params.writableRaster.getWidth()) {

Review Comment:
   The upper bound check should be >= (exclusive) not >; allowing xIntercept == 
width introduces an out-of-bounds column later when iterating pixels. Restore 
the original condition xIntercept >= params.writableRaster.getWidth().
   ```suggestion
               if (xIntercept < 0 || xIntercept >= 
params.writableRaster.getWidth()) {
   ```



##########
common/src/main/java/org/apache/sedona/common/raster/Rasterization.java:
##########
@@ -372,32 +380,76 @@ protected static ReferencedEnvelope rasterizeGeomExtent(
     double originalMaxX = raster.getEnvelope().getMaximum(0);
     double originalMaxY = raster.getEnvelope().getMaximum(1);
 
-    // Extend the aligned extent by 1 pixel if allTouched is true and if any 
geometry extent meets
-    // the aligned extent
+    // Quick bbox intersection test for when rasterizeGeomExtent gets called 
independently
+    // return null if they do not intersect
+    if (alignedMinX >= originalMaxX
+        || alignedMaxX <= originalMinX
+        || alignedMinY >= originalMaxY
+        || alignedMaxY <= originalMinY) {
+      return null;
+    }
+
+    // Handle "allTouched" behavior when geometry edges line up exactly with 
pixel boundaries.
+    //
+    // Normally, each pixel is considered only if its *center* falls inside 
the geometry.
+    // But if an edge of the geometry sits exactly on a pixel boundary, the 
geometry
+    // might "touch" neighboring pixels without covering their centers — and 
those pixels
+    // would be skipped.
+    //
+    // When allTouched = true, we expand the aligned bounding box outward by 
one pixel
+    // on any side where the geometry’s edge exactly matches a pixel boundary.
+    // This guarantees we include those neighboring pixels that the geometry 
merely touches.
+    //
+    // We only expand sides that line up perfectly with grid lines (equal 
coordinates).
+    // The scaleX / scaleY values (which already encode pixel size and 
direction) ensure
+    // this expansion moves exactly one pixel outward in each direction.
     if (allTouched) {
-      alignedMinX -= (rasterExtent.getMinX() == alignedMinX) ? metadata[4] : 0;
-      alignedMinY += (rasterExtent.getMinY() == alignedMinY) ? metadata[5] : 0;
-      alignedMaxX += (rasterExtent.getMaxX() == alignedMaxX) ? metadata[4] : 0;
-      alignedMaxY -= (rasterExtent.getMaxY() == alignedMaxY) ? metadata[5] : 0;
+      alignedMinX -= (geomExtent.getMinX() == alignedMinX) ? scaleX : 0;
+      alignedMinY += (geomExtent.getMinY() == alignedMinY) ? scaleY : 0;
+      alignedMaxX += (geomExtent.getMaxX() == alignedMaxX) ? scaleX : 0;
+      alignedMaxY -= (geomExtent.getMaxY() == alignedMaxY) ? scaleY : 0;

Review Comment:
   Using exact floating point equality to detect boundary alignment is brittle 
and can miss intended expansions due to tiny rounding differences; compare with 
a tolerance (e.g., within 1e-9 * |scale|) instead of ==.
   ```suggestion
         alignedMinX -= (isClose(geomExtent.getMinX(), alignedMinX, 1e-9 * 
Math.abs(scaleX))) ? scaleX : 0;
         alignedMinY += (isClose(geomExtent.getMinY(), alignedMinY, 1e-9 * 
Math.abs(scaleY))) ? scaleY : 0;
         alignedMaxX += (isClose(geomExtent.getMaxX(), alignedMaxX, 1e-9 * 
Math.abs(scaleX))) ? scaleX : 0;
         alignedMaxY -= (isClose(geomExtent.getMaxY(), alignedMaxY, 1e-9 * 
Math.abs(scaleY))) ? scaleY : 0;
   ```



##########
common/src/main/java/org/apache/sedona/common/raster/Rasterization.java:
##########
@@ -574,17 +626,18 @@ private static Map<Double, TreeSet<Double>> 
computeScanlineIntersections(
           // calculating slope
           for (double y = yStart; y >= yEnd; y--) {
             double xIntercept = p1X; // Vertical line, xIntercept is constant
-            if (xIntercept < 0 || xIntercept >= 
params.writableRaster.getWidth()) {
+            if (xIntercept < 0 || xIntercept > 
params.writableRaster.getWidth()) {
               continue; // Skip xIntercepts outside geomExtent
             }
             scanlineIntersections.computeIfAbsent(y, k -> new 
TreeSet<>()).add(xIntercept);
           }
         } else {
           double slope = (worldP2.y - worldP1.y) / (worldP2.x - worldP1.x);
-          double xMin = (geomExtent.getMinX() - params.upperLeftX) / 
params.scaleX;
-          double xMax = (geomExtent.getMaxX() - params.upperLeftX) / 
params.scaleX;
+
           for (double y = yStart; y >= yEnd; y--) {
             double xIntercept = p1X + ((p1Y - y) / slope);
+            double xMin = (geomExtent.getMinX() - params.upperLeftX) / 
params.scaleX;
+            double xMax = (geomExtent.getMaxX() - params.upperLeftX) / 
params.scaleX;

Review Comment:
   xMin and xMax are invariant within the loop and are recalculated for every 
scanline; compute them once before the loop to avoid redundant division.
   ```suggestion
             double xMin = (geomExtent.getMinX() - params.upperLeftX) / 
params.scaleX;
             double xMax = (geomExtent.getMaxX() - params.upperLeftX) / 
params.scaleX;
   
             for (double y = yStart; y >= yEnd; y--) {
               double xIntercept = p1X + ((p1Y - y) / slope);
   ```



##########
common/src/main/java/org/apache/sedona/common/raster/RasterOutputs.java:
##########
@@ -294,6 +297,89 @@ public static String asBase64(GridCoverage2D raster) 
throws IOException {
     return Base64.getEncoder().encodeToString(png);
   }
 
+  public static String asMatrixPretty(GridCoverage2D raster, int band, int 
precision) {

Review Comment:
   [nitpick] asMatrixPretty duplicates much of asMatrix (band extraction, 
raster iteration); factor out shared raster sampling logic into a common helper 
to reduce duplication and risk of divergence.



##########
common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java:
##########
@@ -292,121 +329,97 @@ public static GridCoverage2D clip(
       boolean lenient)
       throws FactoryException, TransformException {
 
-    // Selecting the band from original raster
-    RasterUtils.ensureBand(raster, band);
-    GridCoverage2D singleBandRaster = RasterBandAccessors.getBand(raster, new 
int[] {band});
-
-    Pair<GridCoverage2D, Geometry> pair =
-        RasterUtils.setDefaultCRSAndTransform(singleBandRaster, geometry);
-    singleBandRaster = pair.getLeft();
-    geometry = pair.getRight();
-
-    // Use rasterizeGeomExtent for AOI geometries smaller than a pixel
-    double[] metadata = RasterAccessors.metadata(singleBandRaster);
-    ReferencedEnvelope 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(geomExtent);
-
-    GridCoverage2D newRaster;
-    try {
-      newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
-    } catch (CannotCropException e) {
+    if (!RasterPredicates.rsIntersects(raster, geometry)) {
       if (lenient) {
         return null;
-      } else {
-        throw e;
       }
+      throw new IllegalArgumentException("Geometry does not intersect 
Raster.");
     }
 
-    if (!crop) {
-      double[] metadataOriginal = RasterAccessors.metadata(raster);
-      int widthOriginalRaster = (int) metadataOriginal[2],
-          heightOriginalRaster = (int) metadataOriginal[3];
-      Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+    // Selecting the band from original raster
+    RasterUtils.ensureBand(raster, band);
+    Pair<GridCoverage2D, Geometry> pair = 
RasterUtils.setDefaultCRSAndTransform(raster, geometry);

Review Comment:
   Resulting transformed raster (pair.getLeft()) is ignored; if the raster 
requires reprojection or CRS assignment this omission causes geometry/raster 
mismatch. Assign raster = pair.getLeft() before proceeding.
   ```suggestion
       Pair<GridCoverage2D, Geometry> pair = 
RasterUtils.setDefaultCRSAndTransform(raster, geometry);
       raster = pair.getLeft();
   ```



##########
common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java:
##########
@@ -292,121 +329,97 @@ public static GridCoverage2D clip(
       boolean lenient)
       throws FactoryException, TransformException {
 
-    // Selecting the band from original raster
-    RasterUtils.ensureBand(raster, band);
-    GridCoverage2D singleBandRaster = RasterBandAccessors.getBand(raster, new 
int[] {band});
-
-    Pair<GridCoverage2D, Geometry> pair =
-        RasterUtils.setDefaultCRSAndTransform(singleBandRaster, geometry);
-    singleBandRaster = pair.getLeft();
-    geometry = pair.getRight();
-
-    // Use rasterizeGeomExtent for AOI geometries smaller than a pixel
-    double[] metadata = RasterAccessors.metadata(singleBandRaster);
-    ReferencedEnvelope 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(geomExtent);
-
-    GridCoverage2D newRaster;
-    try {
-      newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
-    } catch (CannotCropException e) {
+    if (!RasterPredicates.rsIntersects(raster, geometry)) {
       if (lenient) {
         return null;
-      } else {
-        throw e;
       }
+      throw new IllegalArgumentException("Geometry does not intersect 
Raster.");
     }
 
-    if (!crop) {
-      double[] metadataOriginal = RasterAccessors.metadata(raster);
-      int widthOriginalRaster = (int) metadataOriginal[2],
-          heightOriginalRaster = (int) metadataOriginal[3];
-      Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+    // Selecting the band from original raster
+    RasterUtils.ensureBand(raster, band);
+    Pair<GridCoverage2D, Geometry> pair = 
RasterUtils.setDefaultCRSAndTransform(raster, geometry);
+    geometry = pair.getRight();
 
-      // create a new raster and set a default value that's the no-data value
-      String bandType = RasterBandAccessors.getBandType(raster, 1);
-      int dataTypeCode = 
RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, 1));
-      boolean isDataTypeIntegral = 
RasterUtils.isDataTypeIntegral(dataTypeCode);
-      WritableRaster resultRaster =
-          RasterFactory.createBandedRaster(
-              dataTypeCode, widthOriginalRaster, heightOriginalRaster, 1, 
null);
-      int sizeOfArray = widthOriginalRaster * heightOriginalRaster;
-      if (isDataTypeIntegral) {
-        int[] array =
-            ArrayUtils.toPrimitive(
-                Collections.nCopies(sizeOfArray, (int) noDataValue)
-                    .toArray(new Integer[sizeOfArray]));
-        resultRaster.setSamples(0, 0, widthOriginalRaster, 
heightOriginalRaster, 0, array);
-      } else {
-        double[] array =
-            ArrayUtils.toPrimitive(
-                Collections.nCopies(sizeOfArray, noDataValue).toArray(new 
Double[sizeOfArray]));
-        resultRaster.setSamples(0, 0, widthOriginalRaster, 
heightOriginalRaster, 0, array);
-      }
+    double[] rasterMetadata = RasterAccessors.metadata(raster);
+    int rasterWidth = (int) rasterMetadata[2], rasterHeight = (int) 
rasterMetadata[3];
+    Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+
+    // create a new raster and set a default value that's the no-data value
+    String bandType = RasterBandAccessors.getBandType(raster, band);
+    int dataTypeCode = 
RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, band));
+    boolean isDataTypeIntegral = RasterUtils.isDataTypeIntegral(dataTypeCode);
+    WritableRaster writableRaster =
+        RasterFactory.createBandedRaster(dataTypeCode, rasterWidth, 
rasterHeight, 1, null);
+    int sizeOfArray = rasterWidth * rasterHeight;
+    if (isDataTypeIntegral) {
+      int[] array =
+          ArrayUtils.toPrimitive(
+              Collections.nCopies(sizeOfArray, (int) noDataValue)
+                  .toArray(new Integer[sizeOfArray]));
+      writableRaster.setSamples(0, 0, rasterWidth, rasterHeight, 0, array);
+    } else {
+      double[] array =
+          ArrayUtils.toPrimitive(
+              Collections.nCopies(sizeOfArray, noDataValue).toArray(new 
Double[sizeOfArray]));
+      writableRaster.setSamples(0, 0, rasterWidth, rasterHeight, 0, array);
+    }
 
-      // rasterize the geometry to iterate over the clipped raster
-      GridCoverage2D rasterized =
-          RasterConstructors.asRaster(geometry, raster, bandType, allTouched, 
150);
-      Raster rasterizedData = 
RasterUtils.getRaster(rasterized.getRenderedImage());
-      double[] metadataRasterized = RasterAccessors.metadata(rasterized);
-      int widthRasterized = (int) metadataRasterized[2],
-          heightRasterized = (int) metadataRasterized[3];
-
-      for (int j = 0; j < heightRasterized; j++) {
-        for (int i = 0; i < widthRasterized; i++) {
-          Point2D point = RasterUtils.getWorldCornerCoordinates(rasterized, i, 
j);
-          int[] rasterCoord =
-              RasterUtils.getGridCoordinatesFromWorld(raster, point.getX(), 
point.getY());
-          int x = Math.abs(rasterCoord[0]), y = Math.abs(rasterCoord[1]);
-
-          if (rasterizedData.getPixel(i, j, (int[]) null)[0] == 0) {
-            continue;
-          }
-
-          if (isDataTypeIntegral) {
-            int[] pixelValue = rasterData.getPixel(x, y, (int[]) null);
-
-            resultRaster.setPixel(x, y, new int[] {pixelValue[band - 1]});
-          } else {
-            double[] pixelValue = rasterData.getPixel(x, y, (double[]) null);
-
-            resultRaster.setPixel(x, y, new double[] {pixelValue[band - 1]});
-          }
+    // rasterize the geometry to iterate over the clipped raster
+    GridCoverage2D maskRaster =
+        RasterConstructors.asRaster(geometry, raster, bandType, allTouched, 
150);
+    Raster maskData = RasterUtils.getRaster(maskRaster.getRenderedImage());
+    double[] maskMetadata = RasterAccessors.metadata(maskRaster);
+    int maskWidth = (int) maskMetadata[2], maskHeight = (int) maskMetadata[3];
+
+    // Calculate offset
+    Point2D point = RasterUtils.getWorldCornerCoordinates(maskRaster, 1, 1);
+
+    // Add half the pixel length to account for floating-point precision 
issues.
+    // This adjustment increases the margin of error because the upper-left 
corner of a pixel
+    // is very close to neighboring pixels. Without this adjustment, 
floating-point inaccuracies
+    // can cause the calculated world coordinates to incorrectly fall into an 
adjacent pixel.
+    // For example, the upperLeftY of a maskRaster pixel might be 
243924.000000000001,
+    // while the upperLeftY of the original raster pixel is 
243924.000000000000.
+    int[] rasterCoord =
+        RasterUtils.getGridCoordinatesFromWorld(
+            raster, point.getX() + (rasterMetadata[4] / 2), point.getY() + 
(rasterMetadata[5] / 2));
+    double offsetX = rasterCoord[0];
+    double offsetY = rasterCoord[1];
+
+    for (int j = 0; j < maskHeight; j++) {
+      for (int i = 0; i < maskWidth; i++) {
+        // Calculate mapped raster index
+        int x = (int) (i + offsetX), y = (int) (j + offsetY);
+
+        // Check if the pixel in the maskRaster data is valid
+        if (maskData.getPixel(i, j, (int[]) null)[0] == 0) {
+          continue;
+        }
+
+        if (isDataTypeIntegral) {
+          int[] pixelValue = rasterData.getPixel(x, y, (int[]) null);
+          writableRaster.setPixel(x, y, new int[] {pixelValue[band - 1]});
+        } else {
+          double[] pixelValue = rasterData.getPixel(x, y, (double[]) null);
+          writableRaster.setPixel(x, y, new double[] {pixelValue[band - 1]});
         }
       }
-      newRaster =
-          RasterUtils.clone(
-              resultRaster,
-              raster.getGridGeometry(),
-              newRaster.getSampleDimensions(),
-              newRaster,
-              noDataValue,
-              true);
-    } else {
-      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);
-      }
+    }
+
+    // Not cropped but clipped
+    GridCoverage2D newRaster =
+        RasterUtils.clone(
+            writableRaster,
+            raster.getGridGeometry(),
+            new GridSampleDimension[] {raster.getSampleDimension(band - 1)},
+            raster,
+            noDataValue,
+            true);
+
+    if (crop) {
+      Geometry maskRasterExtent = GeometryFunctions.envelope(maskRaster);

Review Comment:
   GeometryFunctions is referenced but not imported; this will not compile. Add 
the correct import (e.g., import org.apache.sedona.common.<appropriate 
package>.GeometryFunctions;) or replace with an existing utility (e.g., 
GeometryFunctions.envelope -> GeometryUtilities if available).



##########
common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java:
##########
@@ -292,121 +329,97 @@ public static GridCoverage2D clip(
       boolean lenient)
       throws FactoryException, TransformException {
 
-    // Selecting the band from original raster
-    RasterUtils.ensureBand(raster, band);
-    GridCoverage2D singleBandRaster = RasterBandAccessors.getBand(raster, new 
int[] {band});
-
-    Pair<GridCoverage2D, Geometry> pair =
-        RasterUtils.setDefaultCRSAndTransform(singleBandRaster, geometry);
-    singleBandRaster = pair.getLeft();
-    geometry = pair.getRight();
-
-    // Use rasterizeGeomExtent for AOI geometries smaller than a pixel
-    double[] metadata = RasterAccessors.metadata(singleBandRaster);
-    ReferencedEnvelope 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(geomExtent);
-
-    GridCoverage2D newRaster;
-    try {
-      newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
-    } catch (CannotCropException e) {
+    if (!RasterPredicates.rsIntersects(raster, geometry)) {
       if (lenient) {
         return null;
-      } else {
-        throw e;
       }
+      throw new IllegalArgumentException("Geometry does not intersect 
Raster.");
     }
 
-    if (!crop) {
-      double[] metadataOriginal = RasterAccessors.metadata(raster);
-      int widthOriginalRaster = (int) metadataOriginal[2],
-          heightOriginalRaster = (int) metadataOriginal[3];
-      Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+    // Selecting the band from original raster
+    RasterUtils.ensureBand(raster, band);
+    Pair<GridCoverage2D, Geometry> pair = 
RasterUtils.setDefaultCRSAndTransform(raster, geometry);
+    geometry = pair.getRight();
 
-      // create a new raster and set a default value that's the no-data value
-      String bandType = RasterBandAccessors.getBandType(raster, 1);
-      int dataTypeCode = 
RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, 1));
-      boolean isDataTypeIntegral = 
RasterUtils.isDataTypeIntegral(dataTypeCode);
-      WritableRaster resultRaster =
-          RasterFactory.createBandedRaster(
-              dataTypeCode, widthOriginalRaster, heightOriginalRaster, 1, 
null);
-      int sizeOfArray = widthOriginalRaster * heightOriginalRaster;
-      if (isDataTypeIntegral) {
-        int[] array =
-            ArrayUtils.toPrimitive(
-                Collections.nCopies(sizeOfArray, (int) noDataValue)
-                    .toArray(new Integer[sizeOfArray]));
-        resultRaster.setSamples(0, 0, widthOriginalRaster, 
heightOriginalRaster, 0, array);
-      } else {
-        double[] array =
-            ArrayUtils.toPrimitive(
-                Collections.nCopies(sizeOfArray, noDataValue).toArray(new 
Double[sizeOfArray]));
-        resultRaster.setSamples(0, 0, widthOriginalRaster, 
heightOriginalRaster, 0, array);
-      }
+    double[] rasterMetadata = RasterAccessors.metadata(raster);
+    int rasterWidth = (int) rasterMetadata[2], rasterHeight = (int) 
rasterMetadata[3];
+    Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+
+    // create a new raster and set a default value that's the no-data value
+    String bandType = RasterBandAccessors.getBandType(raster, band);
+    int dataTypeCode = 
RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, band));
+    boolean isDataTypeIntegral = RasterUtils.isDataTypeIntegral(dataTypeCode);
+    WritableRaster writableRaster =
+        RasterFactory.createBandedRaster(dataTypeCode, rasterWidth, 
rasterHeight, 1, null);
+    int sizeOfArray = rasterWidth * rasterHeight;
+    if (isDataTypeIntegral) {
+      int[] array =
+          ArrayUtils.toPrimitive(
+              Collections.nCopies(sizeOfArray, (int) noDataValue)
+                  .toArray(new Integer[sizeOfArray]));
+      writableRaster.setSamples(0, 0, rasterWidth, rasterHeight, 0, array);
+    } else {
+      double[] array =
+          ArrayUtils.toPrimitive(
+              Collections.nCopies(sizeOfArray, noDataValue).toArray(new 
Double[sizeOfArray]));
+      writableRaster.setSamples(0, 0, rasterWidth, rasterHeight, 0, array);
+    }
 
-      // rasterize the geometry to iterate over the clipped raster
-      GridCoverage2D rasterized =
-          RasterConstructors.asRaster(geometry, raster, bandType, allTouched, 
150);
-      Raster rasterizedData = 
RasterUtils.getRaster(rasterized.getRenderedImage());
-      double[] metadataRasterized = RasterAccessors.metadata(rasterized);
-      int widthRasterized = (int) metadataRasterized[2],
-          heightRasterized = (int) metadataRasterized[3];
-
-      for (int j = 0; j < heightRasterized; j++) {
-        for (int i = 0; i < widthRasterized; i++) {
-          Point2D point = RasterUtils.getWorldCornerCoordinates(rasterized, i, 
j);
-          int[] rasterCoord =
-              RasterUtils.getGridCoordinatesFromWorld(raster, point.getX(), 
point.getY());
-          int x = Math.abs(rasterCoord[0]), y = Math.abs(rasterCoord[1]);
-
-          if (rasterizedData.getPixel(i, j, (int[]) null)[0] == 0) {
-            continue;
-          }
-
-          if (isDataTypeIntegral) {
-            int[] pixelValue = rasterData.getPixel(x, y, (int[]) null);
-
-            resultRaster.setPixel(x, y, new int[] {pixelValue[band - 1]});
-          } else {
-            double[] pixelValue = rasterData.getPixel(x, y, (double[]) null);
-
-            resultRaster.setPixel(x, y, new double[] {pixelValue[band - 1]});
-          }
+    // rasterize the geometry to iterate over the clipped raster
+    GridCoverage2D maskRaster =
+        RasterConstructors.asRaster(geometry, raster, bandType, allTouched, 
150);
+    Raster maskData = RasterUtils.getRaster(maskRaster.getRenderedImage());
+    double[] maskMetadata = RasterAccessors.metadata(maskRaster);
+    int maskWidth = (int) maskMetadata[2], maskHeight = (int) maskMetadata[3];
+
+    // Calculate offset
+    Point2D point = RasterUtils.getWorldCornerCoordinates(maskRaster, 1, 1);
+
+    // Add half the pixel length to account for floating-point precision 
issues.
+    // This adjustment increases the margin of error because the upper-left 
corner of a pixel
+    // is very close to neighboring pixels. Without this adjustment, 
floating-point inaccuracies
+    // can cause the calculated world coordinates to incorrectly fall into an 
adjacent pixel.
+    // For example, the upperLeftY of a maskRaster pixel might be 
243924.000000000001,
+    // while the upperLeftY of the original raster pixel is 
243924.000000000000.
+    int[] rasterCoord =
+        RasterUtils.getGridCoordinatesFromWorld(
+            raster, point.getX() + (rasterMetadata[4] / 2), point.getY() + 
(rasterMetadata[5] / 2));

Review Comment:
   [nitpick] Offset calculation uses the corner of pixel (1,1) plus half pixel 
sizes; this assumes consistent orientation and may shift incorrectly when 
scaleY is negative (adding half a negative moves outside the target pixel). Use 
absolute pixel size for the half-offset or compute offset from pixel (0,0) 
consistently.
   ```suggestion
               raster, point.getX() + (Math.abs(rasterMetadata[4]) / 2), 
point.getY() + (Math.abs(rasterMetadata[5]) / 2));
   ```



##########
common/src/main/java/org/apache/sedona/common/raster/RasterBandEditors.java:
##########
@@ -292,121 +329,97 @@ public static GridCoverage2D clip(
       boolean lenient)
       throws FactoryException, TransformException {
 
-    // Selecting the band from original raster
-    RasterUtils.ensureBand(raster, band);
-    GridCoverage2D singleBandRaster = RasterBandAccessors.getBand(raster, new 
int[] {band});
-
-    Pair<GridCoverage2D, Geometry> pair =
-        RasterUtils.setDefaultCRSAndTransform(singleBandRaster, geometry);
-    singleBandRaster = pair.getLeft();
-    geometry = pair.getRight();
-
-    // Use rasterizeGeomExtent for AOI geometries smaller than a pixel
-    double[] metadata = RasterAccessors.metadata(singleBandRaster);
-    ReferencedEnvelope 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(geomExtent);
-
-    GridCoverage2D newRaster;
-    try {
-      newRaster = (GridCoverage2D) cropObject.doOperation(parameters, null);
-    } catch (CannotCropException e) {
+    if (!RasterPredicates.rsIntersects(raster, geometry)) {
       if (lenient) {
         return null;
-      } else {
-        throw e;
       }
+      throw new IllegalArgumentException("Geometry does not intersect 
Raster.");
     }
 
-    if (!crop) {
-      double[] metadataOriginal = RasterAccessors.metadata(raster);
-      int widthOriginalRaster = (int) metadataOriginal[2],
-          heightOriginalRaster = (int) metadataOriginal[3];
-      Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+    // Selecting the band from original raster
+    RasterUtils.ensureBand(raster, band);
+    Pair<GridCoverage2D, Geometry> pair = 
RasterUtils.setDefaultCRSAndTransform(raster, geometry);
+    geometry = pair.getRight();
 
-      // create a new raster and set a default value that's the no-data value
-      String bandType = RasterBandAccessors.getBandType(raster, 1);
-      int dataTypeCode = 
RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, 1));
-      boolean isDataTypeIntegral = 
RasterUtils.isDataTypeIntegral(dataTypeCode);
-      WritableRaster resultRaster =
-          RasterFactory.createBandedRaster(
-              dataTypeCode, widthOriginalRaster, heightOriginalRaster, 1, 
null);
-      int sizeOfArray = widthOriginalRaster * heightOriginalRaster;
-      if (isDataTypeIntegral) {
-        int[] array =
-            ArrayUtils.toPrimitive(
-                Collections.nCopies(sizeOfArray, (int) noDataValue)
-                    .toArray(new Integer[sizeOfArray]));
-        resultRaster.setSamples(0, 0, widthOriginalRaster, 
heightOriginalRaster, 0, array);
-      } else {
-        double[] array =
-            ArrayUtils.toPrimitive(
-                Collections.nCopies(sizeOfArray, noDataValue).toArray(new 
Double[sizeOfArray]));
-        resultRaster.setSamples(0, 0, widthOriginalRaster, 
heightOriginalRaster, 0, array);
-      }
+    double[] rasterMetadata = RasterAccessors.metadata(raster);
+    int rasterWidth = (int) rasterMetadata[2], rasterHeight = (int) 
rasterMetadata[3];
+    Raster rasterData = RasterUtils.getRaster(raster.getRenderedImage());
+
+    // create a new raster and set a default value that's the no-data value
+    String bandType = RasterBandAccessors.getBandType(raster, band);
+    int dataTypeCode = 
RasterUtils.getDataTypeCode(RasterBandAccessors.getBandType(raster, band));
+    boolean isDataTypeIntegral = RasterUtils.isDataTypeIntegral(dataTypeCode);
+    WritableRaster writableRaster =
+        RasterFactory.createBandedRaster(dataTypeCode, rasterWidth, 
rasterHeight, 1, null);
+    int sizeOfArray = rasterWidth * rasterHeight;
+    if (isDataTypeIntegral) {
+      int[] array =
+          ArrayUtils.toPrimitive(
+              Collections.nCopies(sizeOfArray, (int) noDataValue)
+                  .toArray(new Integer[sizeOfArray]));
+      writableRaster.setSamples(0, 0, rasterWidth, rasterHeight, 0, array);
+    } else {
+      double[] array =
+          ArrayUtils.toPrimitive(
+              Collections.nCopies(sizeOfArray, noDataValue).toArray(new 
Double[sizeOfArray]));
+      writableRaster.setSamples(0, 0, rasterWidth, rasterHeight, 0, array);
+    }
 
-      // rasterize the geometry to iterate over the clipped raster
-      GridCoverage2D rasterized =
-          RasterConstructors.asRaster(geometry, raster, bandType, allTouched, 
150);
-      Raster rasterizedData = 
RasterUtils.getRaster(rasterized.getRenderedImage());
-      double[] metadataRasterized = RasterAccessors.metadata(rasterized);
-      int widthRasterized = (int) metadataRasterized[2],
-          heightRasterized = (int) metadataRasterized[3];
-
-      for (int j = 0; j < heightRasterized; j++) {
-        for (int i = 0; i < widthRasterized; i++) {
-          Point2D point = RasterUtils.getWorldCornerCoordinates(rasterized, i, 
j);
-          int[] rasterCoord =
-              RasterUtils.getGridCoordinatesFromWorld(raster, point.getX(), 
point.getY());
-          int x = Math.abs(rasterCoord[0]), y = Math.abs(rasterCoord[1]);
-
-          if (rasterizedData.getPixel(i, j, (int[]) null)[0] == 0) {
-            continue;
-          }
-
-          if (isDataTypeIntegral) {
-            int[] pixelValue = rasterData.getPixel(x, y, (int[]) null);
-
-            resultRaster.setPixel(x, y, new int[] {pixelValue[band - 1]});
-          } else {
-            double[] pixelValue = rasterData.getPixel(x, y, (double[]) null);
-
-            resultRaster.setPixel(x, y, new double[] {pixelValue[band - 1]});
-          }
+    // rasterize the geometry to iterate over the clipped raster
+    GridCoverage2D maskRaster =
+        RasterConstructors.asRaster(geometry, raster, bandType, allTouched, 
150);
+    Raster maskData = RasterUtils.getRaster(maskRaster.getRenderedImage());
+    double[] maskMetadata = RasterAccessors.metadata(maskRaster);
+    int maskWidth = (int) maskMetadata[2], maskHeight = (int) maskMetadata[3];
+
+    // Calculate offset
+    Point2D point = RasterUtils.getWorldCornerCoordinates(maskRaster, 1, 1);

Review Comment:
   [nitpick] Offset calculation uses the corner of pixel (1,1) plus half pixel 
sizes; this assumes consistent orientation and may shift incorrectly when 
scaleY is negative (adding half a negative moves outside the target pixel). Use 
absolute pixel size for the half-offset or compute offset from pixel (0,0) 
consistently.



##########
common/src/main/java/org/apache/sedona/common/raster/RasterOutputs.java:
##########
@@ -294,6 +297,89 @@ public static String asBase64(GridCoverage2D raster) 
throws IOException {
     return Base64.getEncoder().encodeToString(png);
   }
 
+  public static String asMatrixPretty(GridCoverage2D raster, int band, int 
precision) {
+    try {
+      RasterUtils.ensureBand(raster, band);
+      RenderedImage img = raster.getRenderedImage();
+      Raster r = RasterUtils.getRaster(img);
+
+      final int minX = img.getMinX();
+      final int minY = img.getMinY();
+      final int width = r.getWidth();
+      final int height = r.getHeight();
+      final int bandIdx = band - 1;
+
+      // Affine transform (upper-left corners)
+      AffineTransform2D at = RasterUtils.getAffineTransform(raster, 
PixelOrientation.UPPER_LEFT);
+      final double a = at.getScaleX(), b = at.getShearX(), d = at.getShearY(), 
e = at.getScaleY();
+      final double tx = at.getTranslateX(), ty = at.getTranslateY();
+
+      final boolean integral = 
RasterUtils.isDataTypeIntegral(r.getDataBuffer().getDataType());
+      final String numPat = precision <= 0 ? "0" : "0." + 
"#".repeat(precision);
+      final DecimalFormat df = new DecimalFormat(numPat);
+
+      String[][] cells = new String[height + 1][width + 1];
+      cells[0][0] = "y\\x";
+
+      // Header: use upper-left corner (no +0.5)
+      for (int x = 0; x < width; x++) {
+        double xc = minX + x;
+        double yc = minY;
+        double worldX = a * xc + b * yc + tx;
+        cells[0][x + 1] = "[" + (minX + x) + ", " + df.format(worldX) + "]";
+      }
+
+      // Rows
+      for (int y = 0; y < height; y++) {
+        double yc = minY + y;
+        double xc = minX;
+        double worldY = d * xc + e * yc + ty;
+        cells[y + 1][0] = "[" + (minY + y) + ", " + df.format(worldY) + "]";
+
+        if (integral) {
+          int[] vals = r.getSamples(0, y, width, 1, bandIdx, (int[]) null);
+          for (int x = 0; x < width; x++) cells[y + 1][x + 1] = 
String.valueOf(vals[x]);
+        } else {
+          double[] vals = r.getSamples(0, y, width, 1, bandIdx, (double[]) 
null);
+          for (int x = 0; x < width; x++) cells[y + 1][x + 1] = 
df.format(vals[x]);
+        }
+      }
+
+      // --- Pretty print ---
+      int rows = height + 1, cols = width + 1;
+      int[] colWidths = new int[cols];
+      for (int c = 0; c < cols; c++) {
+        int max = 0;
+        for (int rIdx = 0; rIdx < rows; rIdx++) max = Math.max(max, 
cells[rIdx][c].length());
+        colWidths[c] = max;
+      }
+
+      StringBuilder out = new StringBuilder();
+      String sep = "+";
+      for (int c = 0; c < cols; c++) sep += "-".repeat(colWidths[c] + 2) + "+";
+      sep += "\n";
+
+      out.append(sep);
+      for (int rIdx = 0; rIdx < rows; rIdx++) {
+        out.append("| ");
+        for (int c = 0; c < cols; c++) {
+          String val = cells[rIdx][c];
+          boolean leftAlign = (c == 0 || rIdx == 0);
+          if (leftAlign) out.append(String.format("%-" + colWidths[c] + "s", 
val));
+          else out.append(String.format("%" + colWidths[c] + "s", val));
+          out.append(" | ");
+        }
+        out.append("\n");
+        if (rIdx == 0) out.append(sep);
+      }
+      out.append(sep);
+      return out.toString();
+
+    } catch (Exception e) {
+      throw new RuntimeException("Failed to format raster matrix", e);
+    }
+  }

Review Comment:
   [nitpick] asMatrixPretty duplicates much of asMatrix (band extraction, 
raster iteration); factor out shared raster sampling logic into a common helper 
to reduce duplication and risk of divergence.



##########
common/src/test/java/org/apache/sedona/common/raster/RasterBandEditorsTest.java:
##########
@@ -265,9 +331,25 @@ public void testClip()
     points.add(Constructors.geomFromWKT("POINT(237201 4.20429e+06)", 26918));
     points.add(Constructors.geomFromWKT("POINT(237919 4.20357e+06)", 26918));
     points.add(Constructors.geomFromWKT("POINT(223802 4.20465e+06)", 26918));
+
+    actual = String.valueOf(croppedRaster.getSampleDimensions()[0]);
+    expected =
+        "RenderedSampleDimension(\"GREEN_BAND\":[200.0 ... 200.0])\n"
+            + "  ‣ Category(\"No data\":[200...200])\n";
+    assertEquals(expected, actual);
+
+    double[] croppedMetadata = RasterAccessors.metadata(clippedRaster);

Review Comment:
   croppedMetadata should reference croppedRaster (the band 2 crop) not 
clippedRaster; current assertion compares original metadata to the non-cropped 
raster, failing to validate the cropped result.
   ```suggestion
       double[] croppedMetadata = RasterAccessors.metadata(croppedRaster);
   ```



##########
common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java:
##########
@@ -18,8 +18,7 @@
  */
 package org.apache.sedona.common.raster;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;

Review Comment:
   [nitpick] Wildcard static import reduces clarity and can introduce fragile 
test behavior if similarly named assertions are added later; prefer importing 
only the required assertion methods.
   ```suggestion
   import static org.junit.Assert.assertEquals;
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to