Skip to content

fix: honor mask/alpha bands and mask RGB nodata only when all bands match - #16

Merged
TommySquared merged 1 commit into
mainfrom
fix/geotiff-mask-rules
Sep 29, 2026
Merged

TommySquared merged 1 commit into
mainfrom
fix/geotiff-mask-rules

Conversation

@TommySquared

Copy link
Copy Markdown
Collaborator

Problem

_process_rgb treats a pixel as nodata when any of its three bands equals the nodata value. In a file tagged nodata=0, that punches holes in valid pixels that happen to have one zero channel: pure red (255, 0, 0), deep shadows, saturated composites. A fill value fills every band, so the rule should require all three.

Neither _process_rgb nor _process_singleband reads the file's mask band or alpha band. Only the nodata tag and NaN are used, so GeoTIFFs that mark empty pixels with a GDAL internal mask or an alpha band (common in GIS exports) draw those pixels opaque.

Changes

  • RGB nodata rule: transparent only when all three bands equal the nodata value (tag or nodata override). NaN in any band still masks, since that pixel has no color to draw.
  • Mask and alpha bands: both RGB and single-band now honor a per-dataset mask or alpha band via read_masks. Nodata tags are handled exactly as before, so a nodata override still replaces the tag. Files without a mask render as they did.
  • NaN cast: the RGB stretch zeroes masked pixels before the uint8 cast. NaN was reaching astype(np.uint8) (undefined behavior, hidden by the alpha channel, but it raised RuntimeWarning: invalid value encountered in cast).

Behavior change to note: a file that has both a nodata tag and a mask band now hides the union of the two.

Testing

  • New server/tests/test_geotiff_masking.py (7 tests): pure red and shadow pixels stay opaque under nodata=0, all-zero fill is hidden, the override uses the same rule, NaN in one band masks, internal mask and alpha band are honored, and a file with no tag or mask is fully opaque. 5 of the 7 fail on current main.
  • Tested by hand against a running stack built from this branch.
  • The rest of the suite shows the same results as on main locally: most tests error with StreamableHTTPSessionManager .run() can only be called once per instance from the MCP test fixtures, which is unrelated to this change. No existing test covers geotiff_service.py.

🤖 Generated with Claude Code

…atch

- RGB: a pixel is nodata only when all three bands equal the nodata value
  (tag or override). One band that happens to equal it -- pure red, deep
  shadow -- is data. NaN in any band still masks.
- RGB and single-band: honor a per-dataset mask band or alpha band
  (GDAL internal masks, GIS exports). Nodata tags are still handled as
  before, so a nodata override keeps replacing the tag.
- RGB stretch: zero masked pixels before the uint8 cast so NaN never
  reaches it (was undefined behavior, hidden by alpha).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TommySquared
TommySquared merged commit 45a2dcf into main Sep 29, 2026
3 checks passed
@TommySquared
TommySquared deleted the fix/geotiff-mask-rules branch September 29, 2026 00:21
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.

1 participant