diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 7f7e65a5900..ec6eeeef47c 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -222,7 +222,7 @@ exports.valObjects = { any: { description: 'Any type.', requiredOpts: [], - otherOpts: ['dflt'], + otherOpts: ['dflt', 'values'], coerceFunction: function(v, propOut, dflt) { if(v === undefined) propOut.set(dflt); else propOut.set(v); diff --git a/src/plots/mapbox/layout_attributes.js b/src/plots/mapbox/layout_attributes.js index fdf9d97fe07..3aa94e890f4 100644 --- a/src/plots/mapbox/layout_attributes.js +++ b/src/plots/mapbox/layout_attributes.js @@ -57,13 +57,14 @@ module.exports = { ].join(' ') }, style: { - valType: 'string', + valType: 'any', values: ['basic', 'streets', 'outdoors', 'light', 'dark', 'satellite', 'satellite-streets'], dflt: 'basic', role: 'style', description: [ 'Sets the Mapbox map style.', - 'Either input the defaults Mapbox names or the URL to a custom style.' + 'Either input one of the default Mapbox style names or the URL to a custom style', + 'or a valid Mapbox style JSON.' ].join(' ') }, diff --git a/src/plots/mapbox/mapbox.js b/src/plots/mapbox/mapbox.js index 6d53104d8f4..803f77b66b7 100644 --- a/src/plots/mapbox/mapbox.js +++ b/src/plots/mapbox/mapbox.js @@ -41,7 +41,7 @@ function Mapbox(opts) { // state variables used to infer how and what to update this.map = null; this.accessToken = null; - this.styleUrl = null; + this.styleObj = null; this.traceHash = {}; this.layerList = []; } @@ -64,7 +64,7 @@ proto.plot = function(calcData, fullLayout, promises) { if(self.map && (opts.accesstoken !== self.accessToken)) { self.map.remove(); self.map = null; - self.styleUrl = null; + self.styleObj = null; self.traceHash = []; self.layerList = {}; } @@ -90,16 +90,17 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) { gd = self.gd, opts = self.opts; - // mapbox doesn't have a way to get the current style URL; do it ourselves - var styleUrl = self.styleUrl = convertStyleUrl(opts.style); + // store style id and URL or object + var styleObj = self.styleObj = getStyleObj(opts.style); // store access token associated with this map self.accessToken = opts.accesstoken; + // create the map! var map = self.map = new mapboxgl.Map({ container: self.div, - style: styleUrl, + style: styleObj.style, center: convertCenter(opts.center), zoom: opts.zoom, bearing: opts.bearing, @@ -172,11 +173,11 @@ proto.updateMap = function(calcData, fullLayout, resolve, reject) { self.rejectOnError(reject); - var styleUrl = convertStyleUrl(self.opts.style); + var styleObj = getStyleObj(self.opts.style); - if(self.styleUrl !== styleUrl) { - self.styleUrl = styleUrl; - map.setStyle(styleUrl); + if(self.styleObj.id !== styleObj.id) { + self.styleObj = styleObj; + map.setStyle(styleObj.style); map.style.once('load', function() { @@ -402,16 +403,32 @@ proto.getView = function() { }; }; -function convertStyleUrl(style) { - var styleValues = layoutAttributes.style.values; +function getStyleObj(val) { + var styleValues = layoutAttributes.style.values, + styleDflt = layoutAttributes.style.dflt, + styleObj = {}; - // if style is part of the 'official' mapbox values, - // add URL prefix and suffix - if(styleValues.indexOf(style) !== -1) { - return constants.styleUrlPrefix + style + '-' + constants.styleUrlSuffix; + if(Lib.isPlainObject(val)) { + styleObj.id = val.id; + styleObj.style = val; } + else if(typeof val === 'string') { + styleObj.id = val; + styleObj.style = (styleValues.indexOf(val) !== -1) ? + convertStyleVal(val) : + val; + } + else { + styleObj.id = styleDflt; + styleObj.style = convertStyleVal(styleDflt); + } + + return styleObj; +} - return style; +// if style is part of the 'official' mapbox values, add URL prefix and suffix +function convertStyleVal(val) { + return constants.styleUrlPrefix + val + '-' + constants.styleUrlSuffix; } function convertCenter(center) { diff --git a/test/jasmine/tests/mapbox_test.js b/test/jasmine/tests/mapbox_test.js index 1adad1f9f10..eff986c26c0 100644 --- a/test/jasmine/tests/mapbox_test.js +++ b/test/jasmine/tests/mapbox_test.js @@ -55,6 +55,24 @@ describe('mapbox defaults', function() { expect(layoutOut.mapbox._input).toBe(mapbox); }); + it('should accept both string and object style', function() { + var mapboxStyleJSON = { + id: 'cdsa213wqdsa', + owner: 'johnny' + }; + + layoutIn = { + mapbox: { style: 'light' }, + mapbox2: { style: mapboxStyleJSON } + }; + + fullData.push({ type: 'scattermapbox', subplot: 'mapbox2' }); + + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.mapbox.style).toEqual('light'); + expect(layoutOut.mapbox2.style).toBe(mapboxStyleJSON); + }); + it('should fill layer containers', function() { layoutIn = { mapbox: {