Skip to content

Use small bounding box instead of circle for map hit detection#1430

Merged
ktuite merged 1 commit intomasterfrom
ktuite/map_overlap_polygons
Dec 6, 2025
Merged

Use small bounding box instead of circle for map hit detection#1430
ktuite merged 1 commit intomasterfrom
ktuite/map_overlap_polygons

Conversation

@ktuite
Copy link
Member

@ktuite ktuite commented Dec 5, 2025

Closes getodk/central#1455

Removes radial distance check that made use of getClosestPoint.

Longer explanation:

In the function getHits(pixel) in geojson-map.vue, there's a two step process:

1. getFeaturesAtPixel(pixel, ...)

(OpenLayers docs)

Internally, this calls renderer_.forEachFeatureAtCoordinate (source code). This function is based on the rendering/visualization of the features, e.g. the glow/highlight on a line would count, and everything in the upside down tear map marker would count as a hit.

Since we're using webgl vector layers, it calls the webgl renderer's version of that function, which is only capable of returning a single feature.

2. forEachFeatureNearPixel(source, pixel, ...)

This is our own function that makes up for the fact that with webgl, getFeaturesAtPixel only returns one feature, even if the click hits multiple features.

This uses the feature data representation (e.g. the coordinates of a point, or the coordinates representing lines and polygons) instead of their rendering. See how 'featureSource' and 'clusterSource' are passed in.

To get multiple features around a click, we construct a small extent/bounding box and use forEachFeatureIntersectingExtent (docs) to mathematically compare all source features with the extent.

We used to further filter this result (of matching features) by checking features within a circular radius within the bounding box. That involved a call to geometry.getClosestPoint(), which returned the closest point on the boundary of a polygon. If you clicked inside a polygon and the click was not within the hit radius, it wouldn't get flagged as a hit.

Actually, one of the polygons would be a hit because it was detected in the first step, but the second polygon wouldn't be a hit for the above reason.

This PR just removes that circle radius check. If the radius is small (e.g. <10 pixels) the extra hit detection are inside the bounding box and outside the circular radius are negligible.

What has been done to verify that this works as intended?

I tried it on the one known problem case, but we should revisit related problem scenarios, too.

Why is this the best possible solution? Were any other approaches considered?

I was hoping there would be some other trick within OpenLayers, or that getFeaturesAtPixel could be made to return multiple features with webgl layers, but I don't think there's a way. I'm convinced that our 2 step process is necessary. But now it's a bit simpler.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

Should improve map UX a little bit. Hopefully doesn't make other things worse.

Does this change require updates to user documentation? If so, please file an issue here and include the link below.

Before submitting this PR, please make sure you have:

  • run npm run test and npm run lint and confirmed all checks still pass OR confirm CircleCI build passes
  • verified that any code or assets from external sources are properly credited in comments or that everything is internally sourced

@ktuite ktuite requested a review from matthew-white December 5, 2025 20:45
@ktuite ktuite force-pushed the ktuite/map_overlap_polygons branch from 8ddeced to 7f6c3f0 Compare December 6, 2025 01:39
@ktuite ktuite merged commit 49a23c8 into master Dec 6, 2025
7 checks passed
@ktuite ktuite deleted the ktuite/map_overlap_polygons branch December 6, 2025 01:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overlap detection does not account for one polygon containing another

2 participants