Clearing NoData on one raster band changes another band's MapAlgebra results
- Dominant language
- Java
- Stars
- 2.4k
- Forks
- 784
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 58
Description
## Expected behavior
Clearing NoData on band 2 should leave band 1's NoData handling unchanged. If band 1 still reports NoData `0`, MapAlgebra should continue treating its zero-valued pixels as NoData.
This is a follow-up to #3312, tracked separately from the SQL NULL-handling fix in #3311.
## Actual behavior
Starting with a two-band GeoTIFF whose bands both use NoData `0`, clearing only band 2 leaves the band metadata at `(0, null)`, but changes band 1's result for `out = rast[0] + 1;` from `NaN` to `1.0`.
That change happens in memory, before writing the cleared raster. The clear path removes the shared `GC_NODATA` property from the coverage and image, while band 1's sample dimension still declares NoData `0`. Jiffle reads the image property, so it starts treating band 1's zeros as ordinary data.
A subsequent GeoTIFF round-trip also changes `(0, null)` back to `(0, 0)`. The writer uses one dataset-wide NoData value, so mixed per-band states cannot be preserved by the current path.
## Steps to reproduce
Run in JShell with `sedona-common` and its dependencies on the classpath. The first GeoTIFF round-trip creates an input with the image-level NoData property used by real GeoTIFFs. `rast[0]` refers to band 1.
```java
import org.apache.sedona.common.raster.*;
var raster = RasterConstructors.makeEmptyRaster(
2, 20, 20, 0, 0, 1, -1, 0, 0, 4326);
raster = RasterBandEditors.setBandNoDataValue(raster, 1, 0.0);
raster = RasterBandEditors.setBandNoDataValue(raster, 2, 0.0);
var loaded = RasterConstructors.fromGeoTiff(RasterOutputs.asGeoTiff(raster));
var before = MapAlgebra.mapAlgebra(loaded, "d", "out = rast[0] + 1;");
System.out.println(before.getRenderedImage().getData().getSampleDouble(0, 0, 0));
// NaN
var cleared = RasterBandEditors.setBandNoDataValue(loaded, 2, null);
System.out.println(RasterBandAccessors.getBandNoDataValue(cleared, 1));
// 0.0
System.out.println(RasterBandAccessors.getBandNoDataValue(cleared, 2));
// null
var after = MapAlgebra.mapAlgebra(cleared, "d", "out = rast[0] + 1;");
System.out.println(after.getRenderedImage().getData().getSampleDouble(0, 0, 0));
// 1.0 (expected NaN: band 1 was not cleared)
var roundTrip = RasterConstructors.fromGeoTiff(RasterOutputs.asGeoTiff(cleared));
System.out.println(RasterBandAccessors.getBandNoDataValue(roundTrip, 2));
// 0.0 (band 2's cleared NoData is restored)
```
The regression coverage should check both bands after clearing either one, including MapAlgebra before serialization. For GeoTIFF output, rejecting unsupported mixed NoData states would be preferable to silently changing them.
## Sedona version
`2.0.0-SNAPSHOT`, reproduced on #3312 at `2b06236af00c0789c9e0eff69d9562caf6e90209`.
## API type
Java (`sedona-common`; used by the SQL raster functions).
## JRE version
17, GeoTools 33.1.
## Environment
Local macOS. The reproducer does not require Spark.
## Existing issues
- [x] I searched the existing issues and did not find a duplicate.
Contributor guide
Research direction
Start with RasterBandEditors.setBandNoDataValue, RasterBandAccessors, MapAlgebra.mapAlgebra, and the GeoTIFF round-trip in the supplied JShell reproducer. Add regression coverage for clearing either band, checking both metadata and MapAlgebra before serialization; GeoTIFF output should not silently restore a cleared value in an unsupported mixed NoData state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- data
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100