Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] map.queryRenderedFeatures() doesn't account for {text,icon}-offset or {text,icon}-rotate #6075

Closed
mike-marcacci opened this issue Jan 28, 2018 · 15 comments
Assignees
Labels

Comments

@mike-marcacci
Copy link
Contributor

mike-marcacci commented Jan 28, 2018

NOTE: this bug cannot be reproduced in mapbox-gl-js 0.44.0, because it is blocked by this other, more serious bug.

Using v0.43.0, map.queryRenderedFeatures() doesn't account for the icon-rotate layout property of symbol layers, and returns runs as if it were set to 0.

VIEW SCREENCAST

@anandthakker anandthakker self-assigned this Feb 12, 2018
anandthakker pushed a commit that referenced this issue Feb 12, 2018
anandthakker pushed a commit that referenced this issue Feb 12, 2018
Closes #6075

`textPixelRatio` is used to scale tile coordinates to viewport
coordinates; the value that was being passed here, EXTENT / tileSize,
was the reciprocal of the ratio needed -- we were passing 16 instead of
0.0625.

As a result, the corners of the viewport-projected collision box that we
reconstructed here,
https://github.com/mapbox/mapbox-gl-js/blob/f13c86ea356c384fdab31855b9152f5bf5ef97b8/src/symbol/collision_index.js#L322-L325,
were 16^2 times farther from the anchor than they should have been. With
`icon-offset: [0, 0]`, this didn't matter, because the incorrectly large
box would contain the (much smaller) correct box.  But when there's an
offset, it too gets scaled too much, and so we run out of luck.
anandthakker added a commit that referenced this issue Feb 13, 2018
* Add failing test for issue #6075

* Fix incorrect textPixelRatio argument

Closes #6075

`textPixelRatio` is used to scale tile coordinates to viewport
coordinates; the value that was being passed here, EXTENT / tileSize,
was the reciprocal of the ratio needed -- we were passing 16 instead of
0.0625.

As a result, the corners of the viewport-projected collision box that we
reconstructed here,
https://github.com/mapbox/mapbox-gl-js/blob/f13c86ea356c384fdab31855b9152f5bf5ef97b8/src/symbol/collision_index.js#L322-L325,
were 16^2 times farther from the anchor than they should have been. With
`icon-offset: [0, 0]`, this didn't matter, because the incorrectly large
box would contain the (much smaller) correct box.  But when there's an
offset, it too gets scaled too much, and so we run out of luck.
pathmapper pushed a commit to pathmapper/mapbox-gl-js that referenced this issue Feb 13, 2018
* Add failing test for issue mapbox#6075

* Fix incorrect textPixelRatio argument

Closes mapbox#6075

`textPixelRatio` is used to scale tile coordinates to viewport
coordinates; the value that was being passed here, EXTENT / tileSize,
was the reciprocal of the ratio needed -- we were passing 16 instead of
0.0625.

As a result, the corners of the viewport-projected collision box that we
reconstructed here,
https://github.com/mapbox/mapbox-gl-js/blob/f13c86ea356c384fdab31855b9152f5bf5ef97b8/src/symbol/collision_index.js#L322-L325,
were 16^2 times farther from the anchor than they should have been. With
`icon-offset: [0, 0]`, this didn't matter, because the incorrectly large
box would contain the (much smaller) correct box.  But when there's an
offset, it too gets scaled too much, and so we run out of luck.
pathmapper pushed a commit to pathmapper/mapbox-gl-js that referenced this issue Feb 13, 2018
* Add failing test for issue mapbox#6075

* Fix incorrect textPixelRatio argument

Closes mapbox#6075

`textPixelRatio` is used to scale tile coordinates to viewport
coordinates; the value that was being passed here, EXTENT / tileSize,
was the reciprocal of the ratio needed -- we were passing 16 instead of
0.0625.

As a result, the corners of the viewport-projected collision box that we
reconstructed here,
https://github.com/mapbox/mapbox-gl-js/blob/f13c86ea356c384fdab31855b9152f5bf5ef97b8/src/symbol/collision_index.js#L322-L325,
were 16^2 times farther from the anchor than they should have been. With
`icon-offset: [0, 0]`, this didn't matter, because the incorrectly large
box would contain the (much smaller) correct box.  But when there's an
offset, it too gets scaled too much, and so we run out of luck.
@anandthakker
Copy link
Contributor

The actual issue here was due to icon-offset -- see #6135 (comment)

@anandthakker anandthakker changed the title [bug] map.queryRenderedFeatures() doesn't account for icon-rotate [bug] map.queryRenderedFeatures() doesn't account for {text,icon}-offset Feb 14, 2018
@mike-marcacci
Copy link
Contributor Author

mike-marcacci commented Feb 20, 2018

Hi @anandthakker, thanks for working on this. Unfortunately, this isn't fixed in 0.44.1. I've made a fiddle that shows the issue: https://jsfiddle.net/xzdsxdd9/22/

Note that the cursor should be set to "pointer" when hovered over the wrench, and "" otherwise. The wrench is rotated 90 degrees, but the "hover" zone doesn't consider the rotation.

@anandthakker
Copy link
Contributor

Oof, thanks @mike-marcacci. Reopening for the icon-rotate case.

@anandthakker anandthakker reopened this Feb 20, 2018
@anandthakker anandthakker changed the title [bug] map.queryRenderedFeatures() doesn't account for {text,icon}-offset [bug] map.queryRenderedFeatures() doesn't account for {text,icon}-offset or {text,icon}-rotate Feb 20, 2018
@anandthakker anandthakker added this to the 0.45.0 milestone Mar 8, 2018
@anandthakker
Copy link
Contributor

Per chat with @ChrisLoer, the best way to fix this is most likely to use a covering of circles (like we do for line-placed labels) rather than a box.

@santinogue
Copy link

@anandthakker @mike-marcacci is there a workaround for this?

@mike-marcacci
Copy link
Contributor Author

@santinogue there isn't one that I've found. Fortunately, while this bug is live for us, we're in a "closed beta" of sorts, so we've been able to explain how to "click above the handle" to the limited users... but it's definitely a bad experience :-/

Depending on exactly what you're trying to accomplish, you might consider arranging a set of small transparent icons where you would expect the visual area to be, and use them as click/mouse targets...

@mike-marcacci
Copy link
Contributor Author

Hey @anandthakker! I just wanted to check up on this – is this guy planned for an upcoming release by chance? If not, I'd be happy to work on it if you could elaborate on your "covering of circles" strategy.

@anandthakker
Copy link
Contributor

Hey @mike-marcacci - nope, this isn't specifically planned for a release yet. The strategy @ChrisLoer and I had in mind was to use "collision circles" to represent rotated symbol features, rather than viewport-aligned rectangles.

Currently, we only use collision circles for text labels that have symbol-placement: line. This happens in CollisionFeature:

_addLineCollisionCircles(collisionBoxArray: CollisionBoxArray,
, via this call to addSymbol in symbol_layout.js:
bucket.symbolInstances.push(addSymbol(bucket, anchor, line, shapedTextOrientations, shapedIcon, bucket.layers[0],
bucket.collisionBoxArray, feature.index, feature.sourceLayerIndex, bucket.index,
textBoxScale, textPadding, textAlongLine, textOffset,
iconBoxScale, iconPadding, iconAlongLine, iconOffset,
feature, glyphPositionMap, sizes));

So we'd need to refactor these a bit to add an additional path for using a set of collision circles also in the case where we have symbol-placement: point and the symbol is not oriented orthogonal with respect to the viewport.

Also worth noting #5474, which would simplify some of the collision circle logic.

@ChrisLoer
Copy link
Contributor

Agree with @anandthakker that the full fix here is to use collision circles to represent rotated icon geometry -- but just wanted to note that part of the problem may be easier to solve:

  1. When icon-rotate is combined with icon-offset, the center of the icon's collision box gets placed in the wrong place. For a lot of (relatively square) icons, the center position may be the really important part. Fixing this should be easier because it doesn't require us to change the way we represent collision geometry.
  2. ​​icon-rotate changes the shape of the icon's, ie putting a rectangle at an X degree angle relative to the viewport. This is the harder change. It's definitely something we want to support, but it's probably more important for icons that have a wide aspect ratio (so that the "shape change" is dramatic). Just spitballing: as a placeholder fix that doesn't require a new type of geometry, we could be conservative and do collision based on the smallest viewport-aligned rectangle that contains the rotated rectangle.

@ChrisLoer
Copy link
Contributor

I experimented with a simple implementation of this in collision_feature.js, and at least for point placement it works well (the bus icon and the "GeoJSON Source 1" label both have the same anchor, with different offsets and rotations). The "envelope" strategy seems like it might be fine -- 45 degrees is a worst case in terms of being "too conservative", but visually it doesn't seem like the box is wildly big.

screenshot 2018-05-04 19 20 50

I'm going to look into doing the harder part of #4798 (handling line labels) to see if it makes sense to pull into one PR. I'll also take a look at #4861 (handling rotation-alignment: map) to see if the envelope strategy is promising there.

@hyperknot
Copy link

hyperknot commented May 6, 2018

@ChrisLoer, what's the recommended way to use collision circles with queryRenderedFeatures? My idea is to simply use 'circle-opacity': 0, on a fake circle layer, in case I can cover my symbols with a single circle.

How would you cover symbols with multiple circles? Would you add multiple layers with 0 opacity?

I'm trying to find a workaround for #6621

@ChrisLoer
Copy link
Contributor

Hi @hyperknot sorry for the confusion, but "collision circles" here refers to an underlying implementation detail -- there's nothing that's directly accessible to you as an SDK user. The good news is, I think #6621 is mainly a duplicate of a bug that we recently fixed and should ship this week! 😄

ChrisLoer added a commit that referenced this issue May 7, 2018
Fixes issue #6075.
Does not fix collision detection for point symbols with *-rotation-alignment: map, or for line-placed symbols with *-offset set.
ChrisLoer added a commit that referenced this issue May 7, 2018
Fixes issue #6075.
Does not fix collision detection for point symbols with *-rotation-alignment: map, or for line-placed symbols with *-offset set.
ChrisLoer added a commit that referenced this issue May 9, 2018
Fixes issue #6075.
Does not fix collision detection for point symbols with *-rotation-alignment: map, or for line-placed symbols with *-offset set.
@ChrisLoer
Copy link
Contributor

Fixed *-rotate for point-placed symbols in #6631 (didn't make the .45 release, so it'll show up in .46).

@mike-marcacci and @santinogue, will this address your current issues? We're still tracking offsets for line-placed labels separately as #4798 and point labels with rotation-alignment: map in #4861.

@mike-marcacci
Copy link
Contributor Author

mike-marcacci commented May 14, 2018

Hi @ChrisLoer! Thank you so much for working on this. I just built master (making sure it includes #6631) and tested it against the example in the fiddle above, and sadly it didn't work. I've got a little bit of time right now to look into this further, so I'll post back once I have more to offer!

@mike-marcacci
Copy link
Contributor Author

Actually, scratch that. It looks like it built from a stale cache. I can confirm that this is fixed!! Thanks SO much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants