@@ -48,6 +48,7 @@ function Mapbox(gd, id) {
48
48
this . styleObj = null ;
49
49
this . traceHash = { } ;
50
50
this . layerList = [ ] ;
51
+ this . belowLookup = { } ;
51
52
}
52
53
53
54
var proto = Mapbox . prototype ;
@@ -126,6 +127,7 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
126
127
promises = promises . concat ( self . fetchMapData ( calcData , fullLayout ) ) ;
127
128
128
129
Promise . all ( promises ) . then ( function ( ) {
130
+ self . fillBelowLookup ( calcData , fullLayout ) ;
129
131
self . updateData ( calcData ) ;
130
132
self . updateLayout ( fullLayout ) ;
131
133
self . resolveOnRender ( resolve ) ;
@@ -191,12 +193,94 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) {
191
193
promises = promises . concat ( self . fetchMapData ( calcData , fullLayout ) ) ;
192
194
193
195
Promise . all ( promises ) . then ( function ( ) {
196
+ self . fillBelowLookup ( calcData , fullLayout ) ;
194
197
self . updateData ( calcData ) ;
195
198
self . updateLayout ( fullLayout ) ;
196
199
self . resolveOnRender ( resolve ) ;
197
200
} ) . catch ( reject ) ;
198
201
} ;
199
202
203
+ proto . fillBelowLookup = function ( calcData , fullLayout ) {
204
+ var opts = fullLayout [ this . id ] ;
205
+ var layers = opts . layers ;
206
+ var i , val ;
207
+
208
+ var belowLookup = this . belowLookup = { } ;
209
+ var hasTraceAtTop = false ;
210
+
211
+ for ( i = 0 ; i < calcData . length ; i ++ ) {
212
+ var trace = calcData [ i ] [ 0 ] . trace ;
213
+ var _module = trace . _module ;
214
+
215
+ if ( typeof trace . below === 'string' ) {
216
+ val = trace . below ;
217
+ } else if ( _module . getBelow ) {
218
+ // 'smart' default that depend the map's base layers
219
+ val = _module . getBelow ( trace , this ) ;
220
+ }
221
+
222
+ if ( val === '' ) {
223
+ hasTraceAtTop = true ;
224
+ }
225
+
226
+ belowLookup [ 'trace-' + trace . uid ] = val || '' ;
227
+ }
228
+
229
+ for ( i = 0 ; i < layers . length ; i ++ ) {
230
+ var item = layers [ i ] ;
231
+
232
+ if ( typeof item . below === 'string' ) {
233
+ val = item . below ;
234
+ } else if ( hasTraceAtTop ) {
235
+ // if one or more trace(s) set `below:''` and
236
+ // layers[i].below is unset,
237
+ // place layer below traces
238
+ val = 'traces' ;
239
+ } else {
240
+ val = '' ;
241
+ }
242
+
243
+ belowLookup [ 'layout-' + i ] = val ;
244
+ }
245
+
246
+ // N.B. If multiple layers have the 'below' value,
247
+ // we must clear the stashed 'below' field in order
248
+ // to make `traceHash[k].update()` and `layerList[i].update()`
249
+ // remove/add the all those layers to have preserve
250
+ // the correct layer ordering
251
+ var val2list = { } ;
252
+ var k , id ;
253
+
254
+ for ( k in belowLookup ) {
255
+ val = belowLookup [ k ] ;
256
+ if ( val2list [ val ] ) {
257
+ val2list [ val ] . push ( k ) ;
258
+ } else {
259
+ val2list [ val ] = [ k ] ;
260
+ }
261
+ }
262
+
263
+ for ( val in val2list ) {
264
+ var list = val2list [ val ] ;
265
+ if ( list . length > 1 ) {
266
+ for ( i = 0 ; i < list . length ; i ++ ) {
267
+ k = list [ i ] ;
268
+ if ( k . indexOf ( 'trace-' ) === 0 ) {
269
+ id = k . split ( 'trace-' ) [ 1 ] ;
270
+ if ( this . traceHash [ id ] ) {
271
+ this . traceHash [ id ] . below = null ;
272
+ }
273
+ } else if ( k . indexOf ( 'layout-' ) === 0 ) {
274
+ id = k . split ( 'layout-' ) [ 1 ] ;
275
+ if ( this . layerList [ id ] ) {
276
+ this . layerList [ id ] . below = null ;
277
+ }
278
+ }
279
+ }
280
+ }
281
+ }
282
+ } ;
283
+
200
284
var traceType2orderIndex = {
201
285
choroplethmapbox : 0 ,
202
286
densitymapbox : 1 ,
@@ -207,6 +291,10 @@ proto.updateData = function(calcData) {
207
291
var traceHash = this . traceHash ;
208
292
var traceObj , trace , i , j ;
209
293
294
+ // Need to sort here by trace type here,
295
+ // in case traces with different `type` have the same
296
+ // below value, but sorting we ensure that
297
+ // e.g. choroplethmapbox traces will be below scattermapbox traces
210
298
var calcDataSorted = calcData . slice ( ) . sort ( function ( a , b ) {
211
299
return (
212
300
traceType2orderIndex [ a [ 0 ] . trace . type ] -
@@ -598,6 +686,40 @@ proto.setOptions = function(id, methodName, opts) {
598
686
}
599
687
} ;
600
688
689
+ proto . getMapLayers = function ( ) {
690
+ return this . map . getStyle ( ) . layers ;
691
+ } ;
692
+
693
+ // convenience wrapper that first check in 'below' references
694
+ // a layer that exist and then add the layer to the map,
695
+ proto . addLayer = function ( opts , below ) {
696
+ var map = this . map ;
697
+
698
+ if ( typeof below === 'string' ) {
699
+ if ( below === '' ) {
700
+ map . addLayer ( opts , below ) ;
701
+ return ;
702
+ }
703
+
704
+ var mapLayers = this . getMapLayers ( ) ;
705
+ for ( var i = 0 ; i < mapLayers . length ; i ++ ) {
706
+ if ( below === mapLayers [ i ] . id ) {
707
+ map . addLayer ( opts , below ) ;
708
+ return ;
709
+ }
710
+ }
711
+
712
+ Lib . warn ( [
713
+ 'Trying to add layer with *below* value' ,
714
+ below ,
715
+ 'referencing a layer that does not exist' ,
716
+ 'or that does not yet exist.'
717
+ ] . join ( ' ' ) ) ;
718
+ }
719
+
720
+ map . addLayer ( opts ) ;
721
+ } ;
722
+
601
723
// convenience method to project a [lon, lat] array to pixel coords
602
724
proto . project = function ( v ) {
603
725
return this . map . project ( new mapboxgl . LngLat ( v [ 0 ] , v [ 1 ] ) ) ;
0 commit comments