diff --git a/src/ol/layer/MapboxVector.js b/src/ol/layer/MapboxVector.js index bb3f51691b..21639c2231 100644 --- a/src/ol/layer/MapboxVector.js +++ b/src/ol/layer/MapboxVector.js @@ -92,13 +92,16 @@ export function normalizeStyleUrl(url, token) { * Turns mapbox:// source URLs into vector tile URL templates. * @param {string} url The source URL. * @param {string} token The access token. + * @param {string} tokenParam The access token key. * @return {string} A vector tile template. * @private */ -export function normalizeSourceUrl(url, token) { +export function normalizeSourceUrl(url, token, tokenParam) { const mapboxPath = getMapboxPath(url); if (!mapboxPath) { - return url; + return token && url.indexOf(token) === -1 + ? `${url}${url.indexOf('?') === -1 ? '?' : '&'}${tokenParam}=${token}` + : url; } return `https://{a-d}.tiles.mapbox.com/v4/${mapboxPath}/{z}/{x}/{y}.vector.pbf?access_token=${token}`; } @@ -133,6 +136,7 @@ class ErrorEvent extends BaseEvent { * @typedef {Object} SourceObject * @property {string} url The source URL. * @property {SourceType} type The source type. + * @property {Array} [tiles] TileJSON tiles. */ /** @@ -155,8 +159,8 @@ const SourceType = { * style created with Mapbox Studio and hosted on Mapbox, this will look like * 'mapbox://styles/you/your-style'. * @property {string} [accessToken] The access token for your Mapbox style. This has to be provided - * for `mapbox://` style urls. For `https://` and other urls, access keys must be part of the style - * url. + * for `mapbox://` style urls. For `https://` and other urls, any access key must be the last query + * parameter of the style url. * @property {string} [source] If your style uses more than one source, you need to use either the * `source` property or the `layers` property to limit rendering to a single vector source. The * `source` property corresponds to the id of a vector source in your Mapbox style. @@ -284,7 +288,16 @@ class MapboxVectorLayer extends VectorTileLayer { this.sourceId = options.source; this.layers = options.layers; - this.accessToken = options.accessToken; + if (options.accessToken) { + this.accessToken = options.accessToken; + } else { + const parts = options.styleUrl.split(/[?&]/); + if (parts.length > 1) { + const keyValue = parts[parts.length - 1].split('='); + this.accessToken = keyValue[1]; + this.accessTokenParam_ = keyValue[0]; + } + } this.fetchStyle(options.styleUrl); } @@ -384,7 +397,13 @@ class MapboxVectorLayer extends VectorTileLayer { styleSource.url.indexOf('{z}') !== -1 ) { // Tile source url, handle it directly - source.setUrl(normalizeSourceUrl(styleSource.url, this.accessToken)); + source.setUrl( + normalizeSourceUrl( + styleSource.url, + this.accessToken, + this.accessTokenParam_ + ) + ); applyStyle(this, style, sourceIdOrLayersList) .then(() => { source.setState(SourceState.READY); @@ -394,7 +413,19 @@ class MapboxVectorLayer extends VectorTileLayer { }); } else { // TileJSON url, let ol-mapbox-style handle it - setupVectorSource(styleSource, styleSource.url).then((source) => { + if (styleSource.tiles) { + styleSource.tiles = styleSource.tiles.map((url) => + normalizeSourceUrl(url, this.accessToken, this.accessTokenParam_) + ); + } + setupVectorSource( + styleSource, + normalizeSourceUrl( + styleSource.url, + this.accessToken, + this.accessTokenParam_ + ) + ).then((source) => { applyStyle(this, style, sourceIdOrLayersList) .then(() => { this.setSource(source); diff --git a/test/browser/spec/ol/layer/MapboxVector.test.js b/test/browser/spec/ol/layer/MapboxVector.test.js index ec04e4e4c5..e6cda2ca54 100644 --- a/test/browser/spec/ol/layer/MapboxVector.test.js +++ b/test/browser/spec/ol/layer/MapboxVector.test.js @@ -80,14 +80,20 @@ describe('ol/layer/MapboxVector', () => { }, { url: 'https://example.com/source/{z}/{x}/{y}.pbf', - expected: 'https://example.com/source/{z}/{x}/{y}.pbf', + expected: 'https://example.com/source/{z}/{x}/{y}.pbf?token=test-token', + }, + { + url: 'https://example.com/source/{z}/{x}/{y}.pbf?foo=bar', + expected: + 'https://example.com/source/{z}/{x}/{y}.pbf?foo=bar&token=test-token', }, ]; const token = 'test-token'; + const tokenParam = 'token'; for (const c of cases) { it(`works for ${c.url}`, () => { - expect(normalizeSourceUrl(c.url, token)).to.be(c.expected); + expect(normalizeSourceUrl(c.url, token, tokenParam)).to.be(c.expected); }); } }); @@ -116,4 +122,22 @@ describe('ol/layer/MapboxVector', () => { }); }); }); + + describe('Access token', function () { + it('applies correct access token from access_token', function () { + const layer = new MapboxVectorLayer({ + styleUrl: 'mapbox://styles/mapbox/streets-v7', + accessToken: '123', + }); + expect(layer.accessToken).to.be('123'); + expect(layer.accessTokenParam_).to.be(undefined); + }); + it('applies correct access token from url', function () { + const layer = new MapboxVectorLayer({ + styleUrl: '?key=123', + }); + expect(layer.accessToken).to.be('123'); + expect(layer.accessTokenParam_).to.be('key'); + }); + }); });