Fix multiscale labels stretching when zoomed out past the coarsest level - #71
Conversation
The MultiscaleLabelsTileLayer was created with minZoom: -20, so deck.gl kept subdividing the tile grid below the deepest available resolution level. Past that level getTileData clamps to the deepest loader and returns the same data, but the tile bbox keeps doubling — so the bounds formula stretched that fixed data across an ever-larger world rect, far beyond the image extent. This is the "labels switch to an obviously wrong transformation at certain zoom levels" bug (PR #44 only masked a related texture-reuse artifact via unique sublayer ids). Cap minZoom at -(loader.length - 1), matching Viv's MultiscaleImageLayer, so the coarsest real tiles stay correctly placed at any zoom-out. Also add the bbox-culling guards Viv's renderSubLayers applies (skip negative-edge or zero-sized tiles) for defense in depth. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthrough
ChangesMultiscaleLabel tile culling and minZoom cap
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
What
Multiscale labels rendered with an obviously wrong, vertically-stretched, mis-placed transformation once you zoomed out past the deepest resolution level of the pyramid. This is the recurrence of the "labels switch to a wrong transformation at certain zoom levels" bug that #44 was thought to fix.
Why it happened
MultiscaleLabelsTileLayerwas configured withminZoom: -20. deck.gl therefore kept subdividing the tile grid below the coarsest level that actually exists (e.g. a 4-level pyramid only has tiles down to z = −3):getTileDataclampsresolutionto the deepest loader and returns the same data (a fulltileSizetile).bounds.bottom = bbox.bottom— the inflated value. That fixed strip of label data gets stretched across a world rect far taller than the image.This only triggers below a certain zoom, which is exactly the reported symptom. #44 (unique sublayer ids) only masked a separate stale-texture artifact.
Fix
minZoomatMath.round(-(loader.length - 1)), matching Viv'sMultiscaleImageLayer. deck.gl then stops subdividing past the coarsest real level and keeps those tiles correctly placed at any zoom-out.renderSubLayersapplies (skip tiles with negative bbox edges or zero-sized data) as defense in depth.Diagnosis notes
Confirmed in the running demo by instrumenting
renderSubBitmaskLayersand reading the actual per-tile bounds + model-matrix world rects: at z = −3 the two tiles tile world-y 0 → 14950 → 19264 exactly (correct), while at z < −3 the same data inflated to world-y far beyond the image height — the stretch. The fix was verified visually against the1113PMDC1_human_01sample (H&E +cell_labels).Testing
minZoom === -(levels - 1)for 2- and 4-level loaders, plus culling of out-of-extent / zero-sized tiles.pnpm --filter @spatialdata/layers test→ 46 passing;build(vite +tsc --noEmit) clean.Reviewer notes
MIN_LABELS_DISPLAY_ZOOMis retained — still used by the single-scale path.@spatialdata/layershas nodev(vite build --watch) script, unlikecore/react/zarrextra. The demo works because it aliases the package to source, but consumers reading the builtdistwon't see layer edits duringpnpm devwithout a manual build. Not addressed here.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests