You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2026/09/13 06:27:17 UTC

(sedona) branch master updated: [GH-3330] Preserve other raster bands when replacing NoData (#3347)

This is an automated email from the ASF dual-hosted git repository.

jiayuasu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git


The following commit(s) were added to refs/heads/master by this push:
     new 98bb35c19e3 [GH-3330] Preserve other raster bands when replacing NoData (#3347)
98bb35c19e3 is described below

commit 98bb35c19e3934af8cc054092c3ff42aec5e2f17
Author: Jia Yu <ji...@apache.org>
AuthorDate: Sat Sep 12 23:27:11 2026 -0700

    [GH-3330] Preserve other raster bands when replacing NoData (#3347)
---
 .../sedona/common/raster/RasterBandEditors.java    |  1 +
 .../common/raster/RasterBandEditorsTest.java       | 42 ++++++++++++++++++++++
 .../org/apache/sedona/sql/rasteralgebraTest.scala  | 23 +++++++++++-
 3 files changed, 65 insertions(+), 1 deletion(-)

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 eced128c671..77497bdf20e 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
@@ -85,6 +85,7 @@ public class RasterBandEditors {
       int width = RasterAccessors.getWidth(raster);
       WritableRaster wr =
           RasterFactory.createBandedRaster(dataTypeCode, width, height, numBands, null);
+      wr.setRect(rasterData);
       double[] bandData =
           rasterData.getSamples(0, 0, width, height, bandIndex - 1, (double[]) null);
       for (int i = 0; i < bandData.length; i++) {
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 2dcf242f7b5..083f74f7f35 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
@@ -164,6 +164,48 @@ public class RasterBandEditorsTest extends RasterTestBase {
     assertEquals(actualMap.get(15.0), resultMap.get(20.0));
   }
 
+  @Test
+  public void testSetBandNoDataValueWithReplacePreservesOtherBands() throws FactoryException {
+    double[][] originalBands = {{1, 5, 3, 4}, {11, 5, 13, 14}, {21, 22, 5, 24}};
+    double[][] replacedBands = {{1, 99, 3, 4}, {11, 5, 99, 14}, {21, 22, 5, 99}};
+    double[] noDataValues = {5, 13, 24};
+    for (String dataType : new String[] {"b", "us", "s", "i", "f", "d"}) {
+      GridCoverage2D raster =
+          RasterConstructors.makeNonEmptyRaster(
+              3, dataType, 2, 2, 10, 20, 2, -3, 0.25, 0.5, 4326, originalBands);
+      for (int band = 1; band <= 3; band++) {
+        raster = RasterBandEditors.setBandNoDataValue(raster, band, noDataValues[band - 1]);
+      }
+
+      for (int targetBand = 1; targetBand <= 3; targetBand++) {
+        GridCoverage2D result =
+            RasterBandEditors.setBandNoDataValue(raster, targetBand, 99.0, true);
+        assertEquals(raster.getGridGeometry(), result.getGridGeometry());
+        assertEquals(
+            raster.getRenderedImage().getSampleModel().getDataType(),
+            result.getRenderedImage().getSampleModel().getDataType());
+        for (int band = 1; band <= 3; band++) {
+          String context = dataType + ", replacing band " + targetBand + ", checking band " + band;
+          assertArrayEquals(
+              context,
+              band == targetBand ? replacedBands[band - 1] : originalBands[band - 1],
+              MapAlgebra.bandAsArray(result, band),
+              0);
+          assertEquals(
+              context,
+              band == targetBand ? 99.0 : noDataValues[band - 1],
+              RasterBandAccessors.getBandNoDataValue(result, band),
+              0);
+          // Replacing pixels in the result must not mutate the input raster.
+          assertArrayEquals(
+              context, originalBands[band - 1], MapAlgebra.bandAsArray(raster, band), 0);
+          assertEquals(
+              noDataValues[band - 1], RasterBandAccessors.getBandNoDataValue(raster, band), 0);
+        }
+      }
+    }
+  }
+
   @Test
   public void testSetBandNoDataValueWithEmptyRaster() throws FactoryException {
     GridCoverage2D emptyRaster =
diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala b/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
index be98f110a93..b7f78ecb10f 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/rasteralgebraTest.scala
@@ -18,7 +18,7 @@
  */
 package org.apache.sedona.sql
 
-import org.apache.sedona.common.raster.MapAlgebra
+import org.apache.sedona.common.raster.{MapAlgebra, RasterBandAccessors}
 import org.apache.sedona.common.utils.RasterUtils
 import org.apache.spark.sql.expressions.Window
 import org.apache.spark.sql.functions._
@@ -895,6 +895,27 @@ class rasteralgebraTest extends TestBaseScala with BeforeAndAfter with GivenWhen
       assertNull(sparkSession.sql("SELECT RS_SetBandNoDataValue(null, -999)").first().get(0))
     }
 
+    it("Passed RS_SetBandNoDataValue replacement preserves other bands") {
+      val input =
+        Seq((Seq(1.25, 5.0, 3.0, 4.0), Seq(11.0, 5.0, 13.0, 14.0), Seq(21.0, 22.0, 5.0, 24.5)))
+          .toDF("band1", "band2", "band3")
+      val raster = input.selectExpr(
+        "RS_AddBandFromArray(RS_AddBandFromArray(RS_AddBandFromArray(" +
+          "RS_MakeEmptyRaster(3, 'd', 2, 2, 0, 2, 1, -1, 0, 0, 4326), " +
+          "band1, 1, 5d), band2, 2, 13d), band3, 3, 24.5d) AS raster")
+      // Collect the raster itself to cover the four-argument SQL binding and serialization.
+      val result = raster
+        .selectExpr("RS_SetBandNoDataValue(raster, 2, -999d, true)")
+        .first()
+        .getAs[GridCoverage2D](0)
+      assert(MapAlgebra.bandAsArray(result, 1).toSeq == Seq(1.25, 5.0, 3.0, 4.0))
+      assert(MapAlgebra.bandAsArray(result, 2).toSeq == Seq(11.0, 5.0, -999.0, 14.0))
+      assert(MapAlgebra.bandAsArray(result, 3).toSeq == Seq(21.0, 22.0, 5.0, 24.5))
+      assertEquals(5.0, RasterBandAccessors.getBandNoDataValue(result, 1), 0)
+      assertEquals(-999.0, RasterBandAccessors.getBandNoDataValue(result, 2), 0)
+      assertEquals(24.5, RasterBandAccessors.getBandNoDataValue(result, 3), 0)
+    }
+
     it("Passed RS_SetBandNoDataValue clearing a band other than band 1") {
       // The clear consults the target band's no-data value; band 1 having none
       // must not short-circuit the removal on band 2.