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.