Skip to content

Enable layer insertion below symbols #70

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

Merged
merged 4 commits into from
Feb 4, 2025

Conversation

ianthetechie
Copy link
Collaborator

@ianthetechie ianthetechie commented Feb 2, 2025

Issue/Motivation

This adds support for rendering layers below symbols. While one could hard code this for knows styles today, it's not practical to do so for general use cases like in Ferrostar (see stadiamaps/ferrostar#429).

Tasklist

  • Include tests (if applicable) and examples (new or edits)
  • If there are any visual changes as a result, include before/after screenshots and/or videos
  • Add #fixes with the issue number that this PR addresses
  • Update any documentation for affected APIs
  • Update the CHANGELOG

Before:

image

After:

image

Comment on lines +271 to +273
let firstSymbolLayer = mglStyle.layers.first { layer in
layer is MLNSymbolStyleLayer
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Highlighting for discussion. I went for the simplest implementation here. I don't think that there should be much overhead in computing this every time, since this should be a cheap check. But it's technically not needed every time (there may not always be a style layer with the belowSymbols ordering).

If anyone is concerned about this, we could make this lazy with a bit more code.

@ianthetechie
Copy link
Collaborator Author

@Patrick-Kladek anyone from your team interested to review this too?

@sargunv
Copy link

sargunv commented Feb 2, 2025

Y'all might be interested in MapLibre Compose's approach to this, using "layer anchoring". It's more complex but much more flexible; users can insert or replace layers anywhere in the style (for example, I might want some custom layers to go above roads but below borders). Docs (one and two)

Overview copy-pasted from Slack:

MapLibre Compose solves this with “layer anchors”, something like:

Anchor.Below("labels") {
  LineLayer(...)
}

Anchor.Above("border") {
  LineLayer(...)
}

Anchor.Replace("ocean") {
  FillLayer(...)
}

It’s more flexible, but it means you need to know the layer ids of your style, and it’ll break if the layer IDs change. So I like your (simpler) approach: assuming most maps will have symbols near the top, and most use cases will just want to insert below symbols, you solve it cleanly.

I’ll have to see if my public API currently allows for the user to create a getFirstSymbolLayerId() helper; then one could do something like:

Anchor.Below(getFirstSymbolLayerId()) {
  SymbolLayer(...)
}

@ianthetechie
Copy link
Collaborator Author

We already have that functionality, but it's probably hidden in the diff. And of course if your layers are in reference to others not hard coded in JSON, you don't need to hard code IDs.

Similarly we also support defining a source in Swift and having layers reference it directly with no hard coded IDs.

Copy link
Collaborator

@hactar hactar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good improvement. Approving.

I do like the default of line layer being "below symbols", I agree that thats probably what most people will want. But I am also worried that this will confuse users. Why is this the only one that has a "sane" default? How do we decide what the sane default for each layer type is? How do we document this so people aren't confused that things are appearing "out of order"?

Before this pr, things will be rendered in the order they appear in the definition, now the line one will be the only one that will behave differently.

So far I've probably always rendered polygons below symbols too (e.g. to show zones where one can park for free), and circle layers below symbols to create pin compositions - like in the cluster view.

Fill, raster, background and hillshade probably have their own idiomatic position too, somewhere at the bottom of the map style.

I guess what I'm trying to say is we should be careful singling only one layer type out - let's either make them all "idiomatic", or none at all?

@ianthetechie
Copy link
Collaborator Author

I guess what I'm trying to say is we should be careful singling only one layer type out - let's either make them all "idiomatic", or none at all?

That's a good point. Here's the current state of things (exhaustive list):

Layer Default insertion position
Background below others
Circle above others
Fill above others
Line below symbols (proposed)
Symbol above others

I assume everyone agrees that the background layer makes sense at the back. In fact, MapLibre might even render it this way already.

Circle, fill, and symbol default to "above others," which means that additions keep stacking up z-index wise. For symbol, I think this makes sense. You usually want symbols on top.

So I guess circle and fill are the ones up for discussion. Circle is, IIUC, basically a specialized version of fill but for circles rather than polygons; both add shapes with optional fill and stroke properties. Off the top of my head, I can't think of a time where I've actually wanted to obscure symbols with a circle or fill.

I'll pose the question in Slack and see what feedback people have.

@ianthetechie
Copy link
Collaborator Author

Actually, I just came up with a counterexample for the circle layer: markers and clusters often get created this way.

image

@ianthetechie
Copy link
Collaborator Author

@hactar I discussed this today with @Archdoog and we decided it's probably less confusing for everyone to just go with the "above all" behavior.

He also suggested a change which will improve the flexibility of things going forward by refactoring into an enum for positioning relative to another (set of) layer(s). The above/below split is necessary because we couldn't think of a logical behavior or use case for "above symbols" 😂 At the same time, this opens the door to other behaviors in the future.

@ianthetechie
Copy link
Collaborator Author

Had a chat with @Patrick-Kladek today and said it looks good from their side. Wouldn't mind a quick second look from @hactar though since we just decided to change a few things after your review :)

Copy link
Collaborator

@hactar hactar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with this outcome too, and I like the enumed .above 👍

@ianthetechie
Copy link
Collaborator Author

Awesome; thanks!

@ianthetechie ianthetechie merged commit ac5f240 into maplibre:main Feb 4, 2025
2 checks passed
@ianthetechie ianthetechie deleted the insert-below-symbols branch February 4, 2025 12:19
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.

4 participants