maplibre / maplibre-native

MapLibre Native - Interactive vector tile maps for iOS, Android and other platforms.
https://maplibre.org
BSD 2-Clause "Simplified" License
1.06k stars 311 forks source link

`queryRenderedFeatures` should observe width of rendered feature #2781

Open westnordost opened 2 months ago

westnordost commented 2 months ago

When trying to tap on roads or other line features, they are really hard to hit. One has to exactly hit the center of a road to be registered as a tap. Intuitively, if one misses, one will try to zoom in further in order to better hit the feature, but this works even worse as the user continues to need to hit the exact center of a road for it to be registered. See this video. Notice the displayed taps:

https://github.com/user-attachments/assets/3440a865-38d1-4f4b-93f0-7dc3e72ccc84

Describe the solution you'd like

  1. When querying the rendered features, a tap should be registered as one if the tap occurs on the rendered area of the feature. I.e. the width of the rendered line feature should be taken into account, too.

  2. Alternatively, or additionally, there should be a queryRenderedFeatures(coordinate: PointF, radius: Float, ...) method that will return all features that are within the given finger radius in pixels around the given coordinate sorted by the smallest distance to coordinate ascending to account for the size of a finger. (The user does not touch a pixel, it touches a circular area)

Describe alternatives you've considered There's queryRenderedFeatures(coordinates: RectF, ...) that can query an area. So, this can be used to be similar to point 2 above. However, at the end, this can not serve as replacement for either of the above because:

  1. for point 1: the rendered lines have different widths, and different widths per zoom level. So, the radii / areas would need to be of different sizes for different features, which is not possible.

  2. for point 2: the results returned by this method are not sorted by the smallest distance to the coordinate ascending (because there is no center coordinate). But this is necessary to correctly handle the case that there are several lines close to each other: Always the line that is closest to the center of the tap should be selected, rather than any of them.

louwers commented 1 month ago

Could you sort the results from queryRenderedFeatures(coordinate: PointF, radius: Float, ...) yourself perhaps?

westnordost commented 1 month ago

Not practically, because while queryRenderedFeatures returns Features that do contain a Geometry each, these are plain GeoJson geometries and there are no functions available in org.maplibre.geojson for calculating the minimum distance between any GeoJson geometry to a point (the center of the clicked position).

You have to have geo math functions for that internally (in native code) already, otherwise you wouldn't be able to return any features at all, I assume.