line-pattern can render the wrong sprite when a zoom step has two stops inside one integer zoom interval
Summary
Split out from #4787 while fixing the camera-expression line-pattern case.
line-pattern (and the same mechanism for other crossfaded pattern properties) can render the wrong sprite when the pattern expression is a camera step/interpolate with two distinct-valued stops inside a single integer zoom interval.
Reproduction (verified against main)
Style paint:
"line-pattern": ["step", ["zoom"], "A", 12.3, "C", 12.6, "D"]At camera zoom 12.5:
| value | |
|---|---|
Worker patternDependencies / atlas (tile zoom 12) |
{A, D} |
| Main-thread evaluated pair (camera zoom 12.5) | A -> C |
| Sprite actually needed | C |
The atlas never contains C, because patternDependencies is computed at the tile's integer zoom while the pattern is evaluated for rendering at the camera zoom. So the correct sprite is genuinely absent and cannot be recovered at the draw site.
Why it is not fixable in the draw code
src/render/update_pattern_positions_in_program.ts already has a third fallback for a similar mismatch, retrying with layer.getPaintProperty(propertyName). For a camera expression that call returns the expression array, not a sprite name, so the lookup cannot resolve (verified).
Seeding the worker's ZoomHistory does not help either: hasPattern (src/data/bucket/pattern_bucket_features.ts) registers only the worker's own evaluated {from, to} pair, so the atlas content is unchanged (verified).
Suggested direction
Register the sprite the expression resolves to at the camera zoom as a pattern dependency, i.e. compute pattern dependencies in a way that covers the camera zoom rather than only the tile's integer zoom. This is a worker-side change and affects all crossfaded pattern properties, so it needs its own scoping and render coverage.
Impact
line-patternwith fractional-stop camera expressions can paint a neighbouring zoom band's sprite instead of the correct one.- Integer stops at least one zoom level apart (the shape in #4787) are not affected; that case is fixed.
line-dasharrayshares the worker-side evaluation and may have the same class of issue.
Reported during review of the #4787 fix; the draw-site fallback there deliberately narrows rather than papers over this case.
Source: maplibre/maplibre-gl-js