Skip to content

Fix fill_alpha and outline_alpha being ignored in datashader render path#647

Merged
timtreis merged 7 commits intomainfrom
fix/issue-617-datashader-fill-alpha
May 8, 2026
Merged

Fix fill_alpha and outline_alpha being ignored in datashader render path#647
timtreis merged 7 commits intomainfrom
fix/issue-617-datashader-fill-alpha

Conversation

@timtreis
Copy link
Copy Markdown
Member

@timtreis timtreis commented May 8, 2026

Summary

Three related bugs all surface as fill_alpha / outline_alpha being silently ignored or wrong:

  1. Datashader fill_alpha ignored. _ds_shade_continuous and _ds_shade_categorical passed the user alpha into ds.tf.shade(min_alpha=...). min_alpha is a floor on the alpha of non-empty pixels, not a scaling factor — so for shapes (every covered pixel maps to full coverage) the result is always alpha=255 regardless of fill_alpha.
  2. Datashader outline_alpha ignored. _render_ds_outlines had the identical bug.
  3. _set_outline silently remapped outline_alpha=0.0 to 1.0. The arm outline_alpha and outline_alpha == 0.0 short-circuited to False whenever outline_alpha was the literal 0.0 (falsy in Python). When a user paired outline_alpha=0.0 with an explicit outline_color, the 'invisible outline' branch was unreachable and the value got reassigned to (1.0, 1.0) further down.

Because the matplotlib → datashader switch is silent at >10k shapes, large datasets diverged from small ones with no warning.

Fix

  • Add _apply_user_alpha(result, alpha) in _datashader.py that post-multiplies the alpha channel of the shade result by the user-supplied alpha. Same pattern already used by _apply_cmap_alpha_to_datashader_result (issue Issue with set_zero_in_cmap_to_transparent Function Not Making Zero Values Transparent in Spatial Data Plotting #376). Applied in _ds_shade_continuous (both shaded and nan_shaded), _ds_shade_categorical, and _render_ds_outlines. Carries over to the points datashader path automatically.
  • Drop the truthiness gate in _set_outline's zero-alpha branch: outline_alpha == 0.0 alone is correct (naturally False for None or non-zero numerics).

ds.tf.shade(min_alpha=...) is a floor on alpha for non-empty pixels, not
a scaling factor. Passing fill_alpha as min_alpha left shapes at full
opacity regardless of the user's choice. Post-multiply the alpha channel
in the shade result, matching the existing fix-up pattern in
_apply_cmap_alpha_to_datashader_result and the matplotlib path.
@timtreis timtreis changed the title Fix fill_alpha being ignored in datashader render path (closes #617) Fix fill_alpha being ignored in datashader render path May 8, 2026
timtreis added 2 commits May 8, 2026 13:06
Narrow Any to ds.tf.Image | np.ndarray to match the codebase
convention from _datashader_map_aggregate_to_color, and prefer
truthiness over an explicit length check on the AxesImage list.
Same root cause as the fill_alpha fix: outline_alpha was passed only as
ds.tf.shade(min_alpha=...), which is a floor on alpha for non-empty
pixels rather than a scaling factor, so outlines rendered at full
opacity regardless of outline_alpha. Apply the same _apply_user_alpha
post-multiply after the outline shade, with a parametrized regression
test.

Note: outline_alpha=0.0 with an explicit outline_color is silently
remapped to 1.0 by _set_outline (because the truthiness check
`outline_alpha and outline_alpha == 0.0` is always False for 0.0).
That is a separate latent bug and is left out of scope for this PR.
@timtreis timtreis changed the title Fix fill_alpha being ignored in datashader render path Fix fill_alpha and outline_alpha being ignored in datashader render path (closes #617) May 8, 2026
timtreis added 2 commits May 8, 2026 13:17
The arm `outline_alpha and outline_alpha == 0.0` short-circuited to
False when outline_alpha was the literal 0.0 (falsy), so the 'user
doesn't want outlines' branch was unreachable whenever outline_color
was set. The user-facing symptom: passing outline_alpha=0.0 alongside
an explicit outline_color silently rendered an outline at full
opacity. Drop the truthiness gate; the equality check alone is
correct (and naturally False for None or non-zero numerics).

Adds a focused unit test on _set_outline and extends the datashader
outline_alpha regression test to cover the 0.0 case.
Avoids 3 redundant get_array() calls per item via walrus.
@timtreis timtreis changed the title Fix fill_alpha and outline_alpha being ignored in datashader render path (closes #617) Fix fill_alpha and outline_alpha being ignored in datashader render path May 8, 2026
Drop multi-paragraph regression-narrative blocks in favor of one-line
docstrings naming the invariant under test. Strip restating-the-bug
inline comments inside parametrize tables.
Both tests use alpha < 1.0 (0.6 and 0.8). Pre-fix, alpha was ignored
and points rendered fully opaque; the references baked that in. The
fix correctly scales alpha, so the layered yellow-on-blue plot now
shows the expected color blend, and the continuous-color plot shows
the expected partial transparency. Reference images taken from CI
(visual_test_results_hatch-test.py3.14-stable, run 25553060603).
@timtreis timtreis merged commit af10e7e into main May 8, 2026
7 of 8 checks passed
@timtreis timtreis deleted the fix/issue-617-datashader-fill-alpha branch May 8, 2026 11:42
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.37%. Comparing base (1097896) to head (e23f01f).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/spatialdata_plot/pl/_datashader.py 84.61% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #647      +/-   ##
==========================================
+ Coverage   76.35%   76.37%   +0.02%     
==========================================
  Files          11       11              
  Lines        3239     3251      +12     
  Branches      762      764       +2     
==========================================
+ Hits         2473     2483      +10     
- Misses        467      468       +1     
- Partials      299      300       +1     
Files with missing lines Coverage Δ
src/spatialdata_plot/pl/utils.py 66.27% <ø> (ø)
src/spatialdata_plot/pl/_datashader.py 90.25% <84.61%> (-0.59%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants