Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

symbolSortKey does not equally sort icon and text #15964

Closed
johnnewman opened this issue Nov 22, 2019 · 7 comments · Fixed by #16023
Closed

symbolSortKey does not equally sort icon and text #15964

johnnewman opened this issue Nov 22, 2019 · 7 comments · Fixed by #16023
Labels
bug Core The cross-platform C++ core, aka mbgl

Comments

@johnnewman
Copy link

When using MGLSymbolStyleLayer's symbolSortKey expression with textAllowsOverlap as false, text collisions cause the lower-sort features to take priority. Features with a lower sort will draw their text and cause features of a higher sort to hide theirs.

Steps to reproduce

  1. Draw two features on the map that overlap.
  2. Ensure they both have valid icon and text properties.
  3. Set textOptional to true, iconAllowsOverlap to true, and textAllowsOverlap to false.

Expected behavior

When the features collide:

  • the text and icon for the higher-sort feature should appear above the lower-sort feature's icon.
  • the lower-sort feature's text should be hidden since it collides with the high-sort feature.

Actual behavior

The icons are at the correct z-level, but the lower-sort feature's text has caused the higher-sort feature's text to hide.

The attached zip contains a sample Xcode project that demonstrates this issue. You'll just need to run a pod install and supply an API token in the app delegate. The output of the project is captured in the screenshot below. The red feature has a higher sort value, but its text is hidden.

TextLayeringProject.zip

Screen Shot 2019-11-22 at 5 25 32 PM

If I reverse the sort values by multiplying them all by -1, then the red icon appears with its text, but both the text and the icon are drawn below the now higher blue icon:

Screen Shot 2019-11-22 at 5 28 10 PM

Configuration

Mapbox SDK versions: 5.4.0
iOS/macOS versions: 13.2.2
Device/simulator models: 11
Xcode version: 11.2.1

@chloekraw chloekraw added bug Core The cross-platform C++ core, aka mbgl labels Dec 6, 2019
@chloekraw
Copy link
Contributor

Thanks for the report @johnnewman. This probably has the same root cause as mapbox/mapbox-gl-js#8896.

cc @ansis @ahk @asheemmamoowala

@johnnewman
Copy link
Author

@ansis @chloekraw I am still able to reproduce this issue using the sample project with the Mapbox iOS SDK v5.5.1.

For the two features in the sample, the only way I can get the red feature's text to draw above the blue feature is by setting the symbolZOrder to viewport-y. But this means we lose the finer-grained control that we hope to gain by using auto for symbolZOrder and specifying a sort value for symbolSortKey.

My goal is for both the icon and text to be prioritized based upon the symbolSortKey for that feature.

symbolZOrder = viewport-y; symbolSortKey = nil symbolZOrder = auto; symbolSortKey = sort symbolZOrder = auto; symbolSortKey = sort * -1
viewport-y sort sort * -1

Configuration

Mapbox SDK versions: 5.5.1
iOS/macOS versions: 13.3
Device/simulator models: 11
Xcode version: 11.3

@chloekraw
Copy link
Contributor

chloekraw commented Feb 27, 2020

@johnnewman thanks for the comment.

For the two features in the sample, the only way I can get the red feature's text to draw above the blue feature is by setting the symbolZOrder to viewport-y. But this means we lose the finer-grained control that we hope to gain by using auto for symbolZOrder and specifying a sort value for symbolSortKey.

I'm not sure if you misspoke here, but symbolSortKey has no effect when symbolZOrder is set to viewport-y. It must be set to auto in order for sort keys to be taken into consideration.

A few follow-up questions:

  • I can see that iconAllowOverlap is enabled; is textAllowOverlap disabled? It might be helpful to share the rest of your runtime styling properties as well.
  • In the third example, even though the text says that red has a higher sort key, is it true that this example multiplies the same sort from the second example by -1, so the red icon actually has a lower sort key?
  • To be clear, the visual behavior you're hoping to achieve is the leftmost screenshot?

@johnnewman
Copy link
Author

@chloekraw Thank you for your quick response.

Getting the red feature's text to draw over the blue feature with viewport-y was a lucky coincidence. I recognize that doing this will totally drop any sort control that the client has, because symbolSortKey will be ignored. In my tests, viewport-y was the only way to guarantee that text will never draw underneath an icon within the same layer.

layer.text = NSExpression(forKeyPath: "text")
layer.iconImageName = NSExpression(forKeyPath: "icon")
layer.textAnchor = NSExpression(forConstantValue: NSValue(mglTextAnchor: .left))
layer.textJustification = NSExpression(forConstantValue: NSValue(mglTextJustification: .left))
layer.textColor = NSExpression(forConstantValue: UIColor.black)
layer.textAllowsOverlap = NSExpression(forConstantValue: NSNumber(value: false))
layer.iconAllowsOverlap = NSExpression(forConstantValue: NSNumber(value: true))
layer.textOptional = NSExpression(forConstantValue: NSNumber(value: true))
layer.textFontSize = NSExpression(forConstantValue: NSNumber(value: 13))
layer.textOffset = NSExpression(forConstantValue: NSValue(cgVector: CGVector.init(dx: 1, dy: 0)))
layer.symbolSortKey = symbolSortKeyExpression()
  • Yep! The same sort integer was used in both test cases. Here is the snippet from the sample project where you can change it:
    /// Our goal is to get the red pin (sort 100) to appear above the blue pin (sort 0).
    /// Since the red is on top, we also want the red pin's text to draw at the same z-level.
    private func symbolSortKeyExpression() -> NSExpression {
        
        // Red icon on top; blue text appears
        return NSExpression(forKeyPath: "sort")
        
        // Blue icon on top; red text appears
//        return NSExpression(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "sort"), NSExpression(forConstantValue: -1)])
    }
  • Yes, I'm hoping to achieve the leftmost screenshot's behavior, where the feature with the higher sort value draws its icon and text above features with lower sort values. I'm wanting to use auto for symbolZOrder and rely on symbolSortKey to handle the z sorting. My issue is that when I do that, the text is not guaranteed to draw at the same z-level as the icon.

@chloekraw
Copy link
Contributor

chloekraw commented Feb 29, 2020

@johnnewman I see. Unfortunately, symbolSortKey is not currently supported for your use case. The documentation means that when iconAllowOverlap or textAllowOverlap is enabled, a higher sort-key reflects higher priority. When *AllowOverlap is disabled, a lower sort-key reflects higher priority.

This is because when symbols are not allowed to overlap, symbols placed first cause subsequent symbols to be hidden due to collision detection. In contrast, symbols placed first when overlap is enabled are underneath symbols placed later. This is a reflection of how the renderer behaves and we understand it's suboptimal; however, it cannot be easily changed. We would like to investigate rewriting our shaders, placement logic, etc. in a way that may enable a future refactor of symbolSortKey to have consistent sorting based on priority, but this is a very large lift and not prioritized at the moment.

@johnnewman
Copy link
Author

Thank you for the insight @chloekraw. Even though this refactor isn't prioritized at the moment, would it be possible to reopen this ticket so that we have something to track?

@chloekraw
Copy link
Contributor

@johnnewman sure, I've opened a ticket here to track: mapbox/mapbox-gl-js#9368

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Core The cross-platform C++ core, aka mbgl
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants