fix(editable-layers): correct SelectionLayer polygon picking for elongated lassos#635
Open
llamington wants to merge 1 commit into
Open
Conversation
…gated lassos The polygon selection path used `deck.pickObjects()` over the lasso's bounding box, masked by a "blocker" PolygonLayer rendered as a 50 km (`EXPANSION_KM`) buffer around the lasso. When the lasso's bbox extended more than 50 km past its edges (elongated or diagonal lassos), the blocker left gaps in the picking framebuffer and objects in those gaps were incorrectly returned as selected. The approach also depended on a `setTimeout(250)` to wait for the blocker to render and dropped overlapping objects at the same pixel. Replace the GPU-pick + blocker mechanism with a CPU point-in-polygon test: iterate the data of every layer named in `layerIds`, resolve each datum's position via `getPosition` (with `position` / `coordinates` fallbacks), and emit pickingInfos for matches via `@turf/boolean-point-in-polygon`. The test is exact, synchronous, and unconstrained by buffer distance or framebuffer resolution. Removes `EXPANSION_KM`, the blocker `PolygonLayer`, the `pendingPolygonSelection` state, the `setTimeout`, and the `@turf/buffer` / `@turf/difference` dependencies. Selection now supports only layers exposing per-datum positions (e.g. `ScatterplotLayer`, `IconLayer`, `ColumnLayer`) — matching the layer types in the docs example and the repo's own usages. Also addresses the long-standing concave-lasso bug filed as nebula.gl#132.
| return data.flatMap((object, index): SelectionPickingInfo[] => { | ||
| const position = extractPosition(layer, object, index, data); | ||
| if (position === null) return []; | ||
| if (!booleanPointInPolygon(point(position), selectionPolygon)) return []; |
Collaborator
There was a problem hiding this comment.
non-geo may not all pass this conditional check
This works better than the current approach, so its not blocking, but these instances of turf should be triaged under a more careful pass - which may have been the original intention of the GPU-based selections?
CC @eKerney
|
|
||
| type SelectionPickingInfo = Pick<PickingInfo, 'object' | 'layer' | 'index'>; | ||
|
|
||
| function extractPosition( |
Collaborator
There was a problem hiding this comment.
Would we want to extract a 3D position here for any reason? @felixpalmer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes false-positive selection in
SelectionLayer's polygon mode for elongated/diagonal/concave lassos by replacing the GPU-pick + buffered-blocker mechanism with a CPU point-in-polygon test.The bug
When using
SelectionLayer({selectionType: 'polygon', ...}), pins that sit outside the drawn lasso are returned inonSelect({pickingInfos})as if they were inside. The failure is reliable for any lasso whose bounding box extends more than ~50 km past its own edges — long thin lassos, diagonal lassos, and concave shapes.Repro
ScatterplotLayerof pins spread across a continent (e.g. the existing Manhattan demo, but at a wider zoom).SelectionLayerwithselectionType: 'polygon'.onSelect({pickingInfos}): pins clearly outside the drawn polygon are included.Root cause
_selectPolygonObjectsdoes this:turfBuffer(lasso, EXPANSION_KM = 50)polygon with the lasso subtracted (turfDifference).deck.pickObjects({bbox: lassoBoundingBox, layerIds: [blockerId, ...layerIds]})after asetTimeout(250)for the blocker to render.The blocker is what's meant to mask out pins outside the polygon. But:
setTimeout(250)is a race; the source acknowledges it:// HACK, find a better way(line 139).nebula.gl#132 ("Fix concave lasso select bug") has been open since 2018 with a noted TODO: "more proper implementation than the setTimeout hack." This PR is that implementation.
The fix
Replace the GPU-pick + blocker mechanism with a direct CPU point-in-polygon test:
layerIds, iterate itsdata.layer.props.getPosition(...), withobject.position/object.coordinatesfallbacks.@turf/boolean-point-in-polygon(already a dependency) against the polygon.{object, layer, index}for each match.The result is exact, synchronous, and free of the buffer, framebuffer, and timing constraints.
Scope
The new implementation supports layers exposing per-datum positions —
ScatterplotLayer,IconLayer,TextLayer,ColumnLayer, etc. This matches:ScatterplotLayer).examples/editable-layers/sf/app.tsxandexamples/editable-layers/advanced/src/example.tsx(EditableGeoJsonLayer).The previous implementation incidentally returned pickingInfos for
PathLayer/PolygonLayer/ArcLayervia GPU picking, but the results were never meaningful — the 50 km blocker meant the lasso shape barely applied to those geometries anyway. Polygon-mode support for non-point layers was never documented, and no example demonstrates it. If maintainers want explicit support later, that's a separate feature with its own UX decisions (intersection vs containment for paths, etc.).