Open burritojustice opened 7 years ago
cc @matteblair @meetar @nvkelso
(Side note, the example links in this issue aren't working.)
So yeah you've come up against a style precedence issue.
draw
group specificityHow draw rules are resolved and how they were intended to be used:
The child layer overrides the parent layer, so features matching child
will be yellow:
layers:
parent:
draw: { polygons: { color: red } }
child:
draw: { polygons: { color: yellow } }
layers:
# buildings are grey and un-extruded by default
buildings:
draw: { polygons: { color: gray, extrude: false } }
# buildings with a `kind` property are red
kind-buildings:
filter: { kind: true }
draw: { polygons: { color: red } }
# tall buildings are extruded (but they might be gray or red)
tall-buildings:
filter: { height: { min: 100 } }
draw: { polygons: { extrude: true } }
This gives 4 possible variants:
color
, another for extrude
). What happens when multiple matching layer trees try to modify the same properties? The draw groups are still evaluated hierarchically, so the deeper rule in either tree will still win. But what about cases where the same property (e.g. color
) is set in two parallel layers, at the same depth? There is no "right" answer, and so this is discouraged! But, it's also an unfortunate possibility given the syntax we allow. For consistency, they are evaluated in alphabetical order, so a z
layer should win over an a
layer at the same depth.(You mentioned a case where it still seemed a deeper rule was overriding others elsewhere in the tree... we would need to look more closely at the example to see what's happening but I can't access it currently.)
!important
Phew OK so that's how it all works now. What you have encountered is a issue that most people familiar with CSS will recognize (in a slightly different form since Tangram layers aren't exactly like CSS selectors - see how they are resolved here) as the dreaded !important
problem: if a CSS selector has an !important
property, it will override other selectors that would usually have greater precedence.
This of course leads to a war of escalation (from the CSS page linked above):
How to override !important A) Simply add another CSS rule with !important, and either give the selector a higher specificity...
Eventually, multiple conflicting !important
selectors will be resolved the same way that un-important ones are: the last one defined in the file will win (Tangram uses alphabetical because of differences in parsing pipeline, imports, etc.).
CSS !important
is both widely frowned upon, and still used, particularly for "global override" type cases and/or those where you are modifying someone else's complex style... similar to the case you have where you are trying to override a specific aspect of an existing imported basemap:
You should use it when: A) Scenario one: You have a global CSS file that sets visual aspects of your site globally.
(^ I still think this is dubious but it's analogous to your case.)
Though I've been aware of the issue since we first added the ability to match multiple layer trees, I've long been hesitant to add syntax to address it, for all of the reasons it's problematic in CSS. At the same time, we've recently seen a few cases where the same problem is cropping up: if you want to globally override aspects of an imported basemap, you have to create a parallel set of rules for each "terminal" node in the tree (to ensure that your change is the deepest and overrides ancestors). This is cumbersome for us as-is and likely to be too complex for many external users (relies on a lot of style-specific knowledge, in addition to the copy-paste aspects).
If we were to add Tangram syntax to support this, here is what I have experimented with: a precedence
property (similar to label priority
) that is:
!important
, would take a numeric value allowing for multiple levels of precedence override.Here's an example:
layers:
roads:
# major roads are red
major:
filter: { kind: major_road }
draw:
lines:
color: red
width: 10px
# major roads are blue
minor:
filter: { kind: minor_road }
draw:
lines:
color: blue
width: 5px
# surprise! all roads are yellow
surprise:
draw:
lines:
precedence: 1
color: yellow
This certainly opens up a new can of worms, but it provides a way to accommodate the particular use case we've discussed here. I actually have this working in a branch with minimal code changes, but it only really affects the order in which the draw groups in the matching layer trees are merged.
Would a larger or smaller precedence
value "win"? And what is the default of that value when not specified?
Great explanation of the issue here. For now I'll just acknowledge that I've read this and agree that there's a usability issue here - I don't have a solution to propose. I'll be chewing on the precedence
idea and seeing if anything else comes to mind.
I'd like to be able to assign colors to roads in any sublayer in a Mapzen basemap at the top level of the layer, without having to dive into each sublayer.
Doing so now is possible, but it assumes a detailed knowledge of the layer hierarchy which is a challenge for one Mapzen basemap style, never mind all of them.
Right now, it has to be done this way. (This assumes I have a function defining the color.
an example: https://mapzen.com/tangram/play/?scene=https%3A%2F%2Fmapzen.com%2Fapi%2Fscenes%2F22%2F862%2Fresources%2Fblank.yaml#15.0459/37.7598/-122.4131
~https://mapzen.com/tangram/play/?api=22%2F872#15.1500/37.7737/-122.4122~
It would be much simpler and less error prone to do something like this:
Note that I inadvertently got this to work -- in the example below, while the sublayer is
major_road
, the colors apply to all roads, regardless of the sublayer name.https://mapzen.com/tangram/play/?scene=https%3A%2F%2Fmapzen.com%2Fapi%2Fscenes%2F22%2F872%2Fresources%2Fblank.yaml#15.0459/37.7598/-122.4131 ~https://mapzen.com/tangram/play/?api=22/872#14.4667/37.7773/-122.4108~
I don't exactly know why this works. Note also that if you change
z
toa
, some of the roads disappear, so this seems to be parsed in alphabetical order?Anyway, a more stable method of doing this would be useful.