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

More layer matching control with priority and exclusive #705

Merged
merged 2 commits into from
Feb 20, 2019

Conversation

bcamper
Copy link
Member

@bcamper bcamper commented Feb 14, 2019

This PR introduces two new keywords -- priority and exclusive -- for scene layers, to enable greater control over feature filter matching and styling composition.

Background
Tangram layer matching has always been inclusive, meaning a feature can match multiple layers (at multiple levels of the layer style tree), which are then merged to determine the final rendering parameters. Composing complex sub-layer trees can be used to capture both feature and zoom-dependent styling along orthogonal dimensions, for example varying road line widths and colors by separate criteria.

However, two in many ways simpler behaviors have not been easily expressed with the current layer syntax:

  • The priority with which layers are matched has always been implicit. While layer depth takes priority (with an inheritance model), when layers at the same level match (siblings), the tie-breaker has been alphabetical, with the last lexically sorted layer name winning. This is cryptic at best and a significant hindrance to easy authoring where layer priority is important (see Bubble Wrap shield matching).
  • Since multiple sibling layers will match by default, there has been no way to express simple exclusive relationships, for example an ordered list of filter conditions, where the first matching filter should be used, and the others discarded. While it has often been possible to work around this by carefully building filters to be functionally exclusive (for example, defining a filter A, then defining a filter B that negates all of A's conditions, in addition to including its own), this is also an unwieldy approach, and does not serve all cases (such as when the filters have natural overlaps that can't easily be separated in a way that avoids a feature from matching more than once).

New Syntax
The priority and exclusive keywords address these cases, and can be used both separately and together.

  • priority: allows for the explicit definition of the matching order for sibling layers (alphabetical by layer name is still used as the default / fallback).
  • exclusive: marking a layer as exclusive ensures that when that layer matches, no other sibling layers (and their cascading sub-layers, etc.) will; when multiple exclusive layers match, the higher priority layer wins.

For example, exclusive could express a single if-else filter condition:

layers:
  if:
    filter: ...
    exclusive: true
  else:
    ...

When used together, priority and exclusive can be used for chained if-elseif-else filter cases, to ensure that only one of an ordered set of conditions matches:

layers:
  layerA:                 # if matches layerA...
    filter: ...
    priority: 1
    exclusive: true
    draw: ...
  layerB:                 # else if matches layerB...
    filter: ...
    priority: 2
    exclusive: true
    draw: ...
  layerC:                 # else default to layerC...
    priority: 3
    exclusive: true
    draw: ...

Separate from "flow control" cases, priority can simply be used on its own to disambiguate the precedence of multiple layers without reliance on naming conventions.

Implementation
The implementation is a fairly narrow impact on the layer matching logic:

  • When determining layer matching and merging order:
    • Layers with an explicit priority value are matched first, with values sorted from lowest to highest (e.g. priority: 1 is matched before priority: 2).
    • Layers without an explicit priority are matched after, and continue to be sorted lexically.
  • Matching operation:
    • If a layer marked exclusive matches, all further matching at that level in the layer tree ceases and the matching layer is used (matching continues for that exclusive layer's child/descendant layers).
    • In the case that multiple layers are marked exclusive, the first one (according to the order described above) is used.

@bcamper bcamper added this to the v0.18.0 milestone Feb 14, 2019
@bcamper
Copy link
Member Author

bcamper commented Feb 14, 2019

@nvkelso @meetar @matteblair @burritojustice this need for control over matching order and exclusivity recently came to a head for me. Having tried a few different approaches, this is where I've settled. I'm confident the use cases are real, and fairly satisfied with the balance of syntax and implementation footprint here (both less than I feared). But of course interested in opinions, possible alternatives, etc.!

@nvkelso
Copy link
Member

nvkelso commented Feb 14, 2019

I dig it. Like you mentioned, we've had to do some silly gymnastics to accomplish layer filters in the Mapzen house styles and this new syntax would simplify that for the basemaps – and make it easier to style data viz. 2x win!

@nvkelso
Copy link
Member

nvkelso commented Feb 14, 2019

Added Tangram ES sister issue in tangrams/tangram-es#2036.

@matteblair
Copy link
Member

Nice! We had discussed logic like this for Tangram ES, but actually for performance benefits rather than styling control. For layers with many siblings where the filters are effectively exclusive, matching can be much faster if sibling filter evaluation is "short circuited" like this. I can't immediately think of any reason we couldn't implement this in Tangram ES but I'll take a closer look later.

@bcamper
Copy link
Member Author

bcamper commented Feb 15, 2019

Performance is a good point! Yeah, in some ways exclusive: true might have been a better default, but would be problematic for backwards compatibility to change, so will probably end up just adding a lot of these lines to existing styles...

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.

3 participants