mapbox/mapbox-gl-js — pull request #831 by michaelsteffen (2014-11-14 20:23:29+00:00)
Title: Refactor Style.cascade

Newbie here, so apologies in advance for simple errors, style mistakes, if my use case is not representative, etc.

I'm working on a simple experiment that involves showing and hiding many individual features from vector tiles hosted on Mapbox (in my case, bike rides in the Houston, TX area). The supported approach for this seems to be:

1. create individual layers in the map style for each feature
2. create classes that correspond to each layer/feature (e.g. "[feature]_active", or whatever)
3. add or remove those classes, one-by-one or (better) in groups, to show or hide individual features

This is the approach I came to based on the docs. It's also what seems to be used in the [Urban Layers site](http://io.morphocode.com/urban-layers/).

In doing this, however, I noticed that the Style.cascade() function seemed to be a bottleneck to rerendering as new features/layers are shown or hidden.

This pull request proposes two changes to speed up cascade():

1. Splits it into two functions, so that when a new class is added, but the stylesheet is otherwise unchanged, we don't have to recreate buckets, flatten the layer tree, etc.
2. More importantly, refactor the loop that sets paintProps and transProps. With many layers and many classes, this loop runs many times. In my example, I have ~400 rides x 3 layers each for styling. Each ride has an "_active" class. So, when most rides are shown, showing or hiding an additional ride causes the paintProps loop to churn through 1200 layers x 400 classes = ~480,000 iterations. As refactored, the inner loop iterates through layer properties rather then active classes, which is quite a bit faster, at least in my use case.

In my scenario, these two changes result in a large difference in execution time: on my Macbook, cascade(), in its current version, takes >150ms when most of the 400 rides are shown; cascadeClasses(), as written here, takes ~30ms.

I've set up a simple example to show the difference:
- [original](http://michaelsteffen.github.io/rides-gl-speedtest/current.html) (runs in ~58 seconds on my Macbook)
- [refactored](http://michaelsteffen.github.io/rides-gl-speedtest/refactored.html) (runs in ~24 seconds)

Admittedly, this approach may be slightly slower where a map has fewer active classes than it has average properties/layer (maybe < 5-10 active classes?). But in that case, perhaps it is less likely that a developer will want to switch very quickly between the classes, so performance should be less important? And in any event, absolute performance will still hopefully be adequate.