tangram icon indicating copy to clipboard operation
tangram copied to clipboard

Allow rendering last on top instead of first on top

Open pnorman opened this issue 7 years ago • 1 comments

This is a capture of a conversation with myself and @bcamper from Gitter to keep track of it.


@pnorman

Is it possible to make Tangram render the data within a vector tile layer in the same order as most other map rendering libraries so that a feature at the start of the layer is painted over by features later in the layer? By default it's the reverse.

@bcamper

@pnorman this is because Tangram doesn't explicitly use feature order in the tile to determine visual order. The recommendation/expectation of the library is that you set an explicit orderfor each layer, with each layer filtering a collection of features that have the same draw depth (no expected overlap). I realize that some of this logic was previously captured in SQL queries or other server-side selection steps, particularly when rendering with a system like Mapnik. The behavior you see in Tangram is because we are using the GL LESS depth comparison, which means a pixel will only be drawn if its depth is less than the depth value of any previously written pixel. Features are currently processed in order in the tile, so the first feature "wins" and is never drawn over by subsequent features at the same depth (this is efficient because it minimizes "overdraw" / overall pixel fill rate). If the feature looping order was reversed in Tangram, the behavior would be more like what you want. We can consider that (I'd want feedback from the ES/native team, @blair1618), however, I think you might still experience issues around tile boundaries where features from different tiles may overlap -- there's no guaranteed draw order there.

@pnorman

@bcamper when I reversed all my ORDER BY clauses in the SQL generating the vector tiles everything worked as I wanted it to. The issue I'm having is that I do have overlap within a given layer set to an order, and there's no natural way to move it from a SQL ORDER BY to an integer column. In some cases, it might be theoretically impossible.

@bcamper

@pnorman thanks - I will speak with some folks today here about the idea of reversing Tangram's order. I understand why you would want to use the SQL query pattern so I'm open to changing it, but would need to (at a minimum) run unit tests against all of Mapzen's house styles to see if there is any impact (theoretically it only affects ambiguous style rules but I am not positive). For more context, the original intention was that per-feature ordering could be included as a feature property, and referenced within the Tangram YAML. For instance, Mapzen has a sort_rank property in our tiles which is used for this, allowing features to have many different interleaved orders within a single layer (in retrospect, "layer" is a misnomer in Tangram, it is more like a selection of features to which a set of styling is applied). See https://mapzen.com/documentation/vector-tiles/layers/#feature-ordering. Then in the scene YAML you can do order: function() { return feature.sort_rank }.

@pnorman

@bcamper it's not clear to me that I could do that in the scene yaml because I need to end up with ordering first by style, then by priority within the layer. As an example, say I have VTs with VT layers foo and bar (layer order within tiles does not matter), and foo has objects [d, c, b, a] and bar has the one object [baz]. Assume that all of foo's objects are the same except for name and geometry, and the desired visibility of objects with the topmost one first is [bar, a, b, c, d]. With other renderers, I could render features from the foo layer in order, so a is the topmost object, then render features from the bar layer on top of that. The visibility is the one desired. If I reversed the order of the objects in foo layer then in tangram I could do something like

layers:
foo:
  draw:
    polygons:
      order: 200
      ...
bar:
  draw:
    polygons:
      order: 100
      ...

The visibility of the objects is again what I want. But I can't reverse the order of the foo layer because that breaks anyone using the tiles with Mapnik or Mapbox GL, and I may not have control of the vector tile source. If I do have control of the vector tile source, keep the order [d, c, b, a] and add a sort_rank property to the objects such that a.sort_rank = 4, b.sort_rank = 3, c.sort_rank = 2, d.sort_rank = 1, I could then do

layers:
foo:
  draw:
    polygons:
      order: 200
      ...
bar:
  draw:
    polygons:
      order: function() { return 100 + feature.sort_rank }
      ...

This should work in this case, but would break down if I had more than 100 features, because then 100 + feature.sort_rank exceeds 200 and renders on top of foo. If I could do a multi-component order (e.g. an array) I could do something like order: function() { return [100, feature.sort_rank] }, but I'd still need to be able to change the vector tile source to do this. For what I really want, I'd need something for the order within its layer, e.g. order: function() { return [100, feature.fake_variable_for_order_in_layer] }, then I wouldn't need to add a property to the vector tiles.

@bcamper

@pnorman thanks for the detailed example! we'll get back to you on this

@pnorman

Thinking about it, being able to order by multiple components (equivalent to SQL ORDER BY a, b) could be generally useful

pnorman avatar Dec 08 '16 00:12 pnorman

After having written a bunch of queries, I find the Tangram required ordering (topmost first) more natural one to write, and is consistent between labeling layers and fill layers.

I still think it's important to be able to use topmost last layers, because there are so many of them out there, and it's what Mapnik uses.

pnorman avatar Jan 18 '17 23:01 pnorman