Skip to content

Clearing NoData on one raster band changes another band's MapAlgebra results #3324

Description

@jiayuasu

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.

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

  • I searched the existing issues and did not find a duplicate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions