Skip to content

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

Description

@james-willis

Expected behavior

When RS_AsRaster is called with a noDataValue, pixels not covered by the geometry should be set to that value, so the output distinguishes "burned" from "background". This is what PostGIS ST_AsRaster does (nodataval is assigned to cells not touched by the geometry), and the GDAL equivalent (gdal_rasterize -init <nodata> -a_nodata <nodata>, or rasterio.features.rasterize(fill=nodata)).

Actual behavior

The noDataValue is only attached as band metadata. Background pixels keep Java's zero-initialized default, so they come out as 0 — a perfectly valid pixel value — while the band claims its nodata is something else.

A polygon with a hole makes the consequence concrete. Repro on a 7x6 grid (Sedona 1.9.0, Spark 4.0), burning 1 with nodata 9:

SELECT RS_BandAsArray(RS_AsRaster(
  ST_GeomFromWKT('POLYGON ((100.2 499.5, 113.8 499.5, 113.8 482.4, 100.2 482.4, 100.2 499.5),
                           (104.1 496.9, 110.2 496.9, 110.2 487.8, 104.1 487.8, 104.1 496.9))'),
  RS_MakeEmptyRaster(1, 'B', 7, 6, 100.0, 500.0, 2.0, -3.0, 0.0, 0.0, 0),
  'B', false, 1.0, 9.0, false), 1)

Sedona returns (reshaped to 6x7, RS_BandNoDataValue reports 9.0):

1 1 1 1 1 1 1
1 1 0 0 0 1 1
1 1 0 0 0 1 1
1 1 0 0 0 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1

PostGIS and GDAL/rasterio return:

1 1 1 1 1 1 1
1 1 9 9 9 1 1
1 1 9 9 9 1 1
1 1 9 9 9 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1

The pixel selection itself is identical across engines here (all rings are axis-aligned), so the divergence is purely the background fill. The hole shows why this can't be worked around by choosing a tighter extent: the uncovered region is interior to the geometry, and it reads back as valid data 0 rather than nodata.

Root cause

RasterConstructors.asRaster burns the geometry into the WritableRaster returned by Rasterization.rasterize (whose backing Java array is zero-initialized), then only records the nodata as metadata via setBandNoDataValue — nothing ever writes the background (RasterConstructors.java#L133-L149 @ 0299fa8):

List<Object> objects =
    Rasterization.rasterize(geom, raster, pixelType, value, useGeometryExtent, allTouched);

WritableRaster writableRaster = (WritableRaster) objects.get(0);
...
if (noDataValue != null) {
  resultRaster = RasterBandEditors.setBandNoDataValue(resultRaster, 1, noDataValue);
}

This is entirely Sedona-side (no geotools constraint): the fix is to initialize the raster's pixels to noDataValue before burning whenever one is provided.

Impact

Any downstream consumer that respects nodata (stats, map algebra, export to GeoTIFF and reading in other tools) treats the entire background as valid zeros. When 0 is a legitimate data value there is no way to tell background from data, despite the caller having explicitly declared a nodata value.

Affected versions: reproduced on 1.9.0 (released jars); the same code is present on current master (0299fa8).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions