Skip to content

[GH-3112] RS_AsRaster fills uncovered pixels with noDataValue#3114

Open
james-willis wants to merge 6 commits into
apache:masterfrom
james-willis:fix/gh-3112-nodata-background
Open

[GH-3112] RS_AsRaster fills uncovered pixels with noDataValue#3114
james-willis wants to merge 6 commits into
apache:masterfrom
james-willis:fix/gh-3112-nodata-background

Conversation

@james-willis

@james-willis james-willis commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Did you read the Contributor Guide?

Is this PR related to a ticket?

What changes were proposed in this PR?

RS_AsRaster previously only attached noDataValue as band metadata; pixels not covered by the geometry kept the allocation default of 0, a valid pixel value indistinguishable from data. Now Rasterization fills the freshly-allocated raster with the noDataValue before burning the geometry, so uncovered pixels (including interior holes) read back as the declared nodata — matching PostGIS ST_AsRaster (nodataval fills untouched cells) and the gdal_rasterize -init <nodata> -a_nodata <nodata> idiom. Passing no noDataValue keeps the current all-zero background.

The fill happens before burning (not after) because a burn value of 0 would otherwise be indistinguishable from background. RS_SetValues internally rasterized its geometry mask with the band's nodata attached; since it reads that raster purely as a mask where 0 means "outside the geometry", it now uses the overload without a nodata, preserving its behavior exactly.

Existing test expectations that asserted 0 backgrounds alongside a non-zero noDataValue are updated to expect the nodata — those expectations were pinning the bug from #3112.

How was this patch tested?

  • New testAsRasterBackgroundIsNoDataValue: a polygon with an interior hole where the hole and the outside must read back as the nodata; a value = 0 burn that must stay distinguishable from the nodata background; the geometry-extent output path; and the null-nodata default keeping 0. The test was written first and fails on master.
  • Full common module suite passes. PixelFunctionEditorsTest (RS_SetValues), RasterBandAccessorsTest (zonal stats, which passes null nodata and relies on 0 backgrounds), and RasterBandEditorsTest (RS_Clip, which never passes a nodata) pass unchanged — the consumers that depend on 0-backed masks are unaffected.

Did this PR include necessary documentation updates?

  • Yes, I have updated the documentation.

@james-willis
james-willis marked this pull request as ready for review July 17, 2026 08:25
@james-willis
james-willis requested a review from jiayuasu as a code owner July 17, 2026 08:25
@james-willis
james-willis requested a review from prantogg July 17, 2026 17:51
Comment thread docs/api/sql/Raster-Operators/RS_AsRaster.md Outdated
@jiayuasu jiayuasu added this to the sedona-1.9.1 milestone Jul 19, 2026
# Conflicts:
#	common/src/test/java/org/apache/sedona/common/raster/RasterConstructorsTest.java
@james-willis
james-willis requested a review from paleolimbot July 20, 2026 20:57
…ead of coercing

Validate the noDataValue against the target pixel type before it is used as
the background fill and the band nodata metadata. An out-of-range value (for
example -1.0 or 300.0 on an unsigned 8-bit band) or a fractional value on an
integer band would previously be silently coerced into the sample, so the
background read back as data while the recorded nodata disagreed. Reject such
a value with an IllegalArgumentException naming the value and pixel type, and
reuse the validated value for both the fill and the metadata.
…aValue omitted

The overloads without a noDataValue argument now inherit the reference
raster's first band nodata value, using it both as the output band's
nodata metadata and as the fill for the pixels the geometry does not
cover, and error when the reference band has no nodata value to inherit.
An explicit null noDataValue on the widest overload still opts out of a
nodata value and keeps a zero background.

RS_Clip and RS_SetValues rasterize a 0-background mask, so they now pass
an explicit null noDataValue to keep that zero background instead of
relying on the omitted-argument default.
"noDataValue %s is not representable in pixel type '%s' (32-bit float, valid range %s..%s)",
noDataValue, pixelType, -Float.MAX_VALUE, Float.MAX_VALUE));
}
return noDataValue;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we round-trip this through float before returning it? This passes validation:

RasterConstructors.asRaster(geom, ref, "F", false, 1d, 0.1d, false)

but then fails while constructing the nodata metadata with Range [0.1 .. 0.1] is not valid. Returning (double) (float) noDataValue makes the stored pixel and metadata agree. A test with 0.1 should catch it.

* or zero background keeps the zero-initialized allocation as-is.
*/
private static void fillBackground(WritableRaster writableRaster, Double backgroundValue) {
if (backgroundValue == null || backgroundValue == 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backgroundValue == 0 also matches -0.0. For F/D, that leaves the zero-initialized pixels as +0.0 while the nodata metadata remains -0.0. RS_Count(..., true) uses Double.compare, so the hole is counted as data (9 instead of 8 in the 3x3 polygon-with-hole repro). Could we either normalize signed zero before both uses, or skip only positive zero here?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RS_AsRaster: pixels not covered by the geometry get value 0 instead of noDataValue

3 participants