Give map taps to the pin they landed on - #69
Merged
Conversation
MapLibre reports a press on the pin layer by handing over every feature rendered within a 44pt box around the touch, and a pin's queryable area is its whole image, shadow padding included — so a tap near two pins comes back with both, in an order the library doesn't define. Taking the first of them gave the tap to an arbitrary pin: often a neighbour a pin's width away. Nor was there a pin "on top" to prefer instead: every unselected pin had the same sort key, leaving the stacking of two overlapping pins to the order their features happened to arrive in. A pin you couldn't see could take the tap and nothing was wrong. So stack the pins deliberately, by latitude, and measure the candidates against where they are really drawn: the pin whose outline the touch is inside wins, the front one where two overlap, the nearest otherwise. A touch with only one candidate is still taken as-is, so an isolated pin stays as forgiving to hit as MapLibre's hitbox makes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Tapping map pins was unreliable in two ways: a pin roughly a pin's width away would take the tap, and so would a pin buried underneath the one being aimed at.
Both came from how the press was resolved. MapLibre reports a press on the pin layer by handing over every feature rendered within a 44pt box around the touch (
MLRNPressableSource.DEFAULT_HITBOX), and a pin's queryable area is its whole image — shadow padding included, ~67pt — so a tap near two pins routinely comes back with both, in an order the library doesn't define. We tookfeatures[0].There was also no front pin to prefer instead:
symbol-sort-keywas0for every unselected pin, so which of two overlapping pins drew on top came down to the order their features happened to arrive in the source data.Changes
symbol-sort-keynow comes from each pin's latitude, so southern pins draw over the ones behind them as on any map, with the selected pin above the lot.src/map/pinHitTest.ts— measures a tap against the pin's real outline. The drawn pin is exactly the convex hull of a circle and its tip (the three rounded corners share the square's centre, and the two sides at the tip are tangents to it), so containment is exact geometry rather than a bounding box. The pin whose outline the touch is inside wins; where two overlap, the one drawn on top; otherwise the nearest.handlePinPressprojects the candidates throughmapRef.projectand asks that module. A press with a single candidate is taken as-is with no round trip, so an isolated pin stays exactly as forgiving to hit as before.A sort key can't depend on the camera, so with the map rotated far enough the latitude stacking reads upside down; the hit test uses the same key, so it always agrees with what's drawn.
Testing
tscandbiomeclean.http://10.0.2.2:4000, so with no backend running there are no pins to tap.