Skip to content

Commit

Permalink
fix(VectorTileSource): failing to open mapbox url format.
Browse files Browse the repository at this point in the history
  • Loading branch information
gchoqueux committed Mar 9, 2021
1 parent 035701b commit b6dd383
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 6 deletions.
87 changes: 87 additions & 0 deletions src/Parser/MapBoxUrlParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const urlRe = /^(\w+):\/\/([^/?]*)(\/[^?]+)?\??(.+)?/;

const config = {
API_URL: 'https://api.mapbox.com',
REQUIRE_ACCESS_TOKEN: true,
ACCESS_TOKEN: null,
};

function formatUrl(obj) {
const params = obj.params.length ? `?${obj.params.join('&')}` : '';
return `${obj.protocol}://${obj.authority}${obj.path}${params}`;
}

function makeAPIURL(urlObject, accessToken) {
const apiUrlObject = parseUrl(config.API_URL);
urlObject.protocol = apiUrlObject.protocol;
urlObject.authority = apiUrlObject.authority;

if (urlObject.protocol === 'http') {
const i = urlObject.params.indexOf('secure');
if (i >= 0) {
urlObject.params.splice(i, 1);
}
}

if (apiUrlObject.path !== '/') {
urlObject.path = `${apiUrlObject.path}${urlObject.path}`;
}

if (!config.REQUIRE_ACCESS_TOKEN) { return formatUrl(urlObject); }

accessToken = accessToken || config.ACCESS_TOKEN;
if (!accessToken) { throw new Error('An API access token is required'); }
if (accessToken[0] === 's') { throw new Error('Use a public access token (pk.*), not a secret access token (sk.*).'); }

urlObject.params = urlObject.params.filter(d => d.indexOf('access_token') === -1);
urlObject.params.push(`access_token=${accessToken}`);
return formatUrl(urlObject);
}

function isMapboxURL(url) {
return url.indexOf('mapbox:') === 0;
}

function parseUrl(url) {
const parts = url.match(urlRe);
if (!parts) {
throw new Error('Unable to parse URL object');
}
return {
protocol: parts[1],
authority: parts[2],
path: parts[3] || '/',
params: parts[4] ? parts[4].split('&') : [],
};
}

function normalizeSpriteURL(url, format, extension, accessToken) {
const urlObject = parseUrl(url);
if (!isMapboxURL(url)) {
urlObject.path += `${format}${extension}`;
return formatUrl(urlObject);
}
urlObject.path = `/styles/v1${urlObject.path}/sprite${format}${extension}`;
return makeAPIURL(urlObject, accessToken);
}

function normalizeSourceURL(url, accessToken) {
if (!isMapboxURL(url)) { return url; }
const urlObject = parseUrl(url);
urlObject.path = `/v4/${urlObject.authority}.json`;
urlObject.params.push('secure');
return makeAPIURL(urlObject, accessToken);
}

function normalizeStyleURL(url, accessToken) {
if (!isMapboxURL(url)) { return url; }
const urlObject = parseUrl(url);
urlObject.path = `/styles/v1${urlObject.path}`;
return makeAPIURL(urlObject, accessToken);
}

export default {
normalizeStyleURL,
normalizeSourceURL,
normalizeSpriteURL,
};
31 changes: 25 additions & 6 deletions src/Source/VectorTilesSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { featureFilter } from '@mapbox/mapbox-gl-style-spec';
import Style from 'Core/Style';
import TMSSource from 'Source/TMSSource';
import Fetcher from 'Provider/Fetcher';
import urlParser from 'Parser/MapBoxUrlParser';

function toTMSUrl(url) {
return url.replace(/\{/g, '${');
Expand Down Expand Up @@ -33,12 +34,24 @@ class VectorTilesSource extends TMSSource {
* JSON style directly.
* @param {string} [source.sprite] - The base URL to load informations about
* the sprite of the style. If this is set, it overrides the `sprite` value
* of the `source.style`.
* of the `source.style`. A style's sprite property supplies a URL template
* for loading small images.
* ```js
* {
* sprite: 'http//:xxxxx/maps/sprites/'
* }
* ```
* A valid sprite source must supply two types of files:
* * An index file, which is a JSON document containing a description of each image contained in the sprite.
* * Image files, which are PNG images containing the sprite data.
*
* For more specification : [the Mapbox sprite Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sprite/)
*
* @param {string} [source.url] - The base URL to load the tiles. If no url
* is specified, it reads it from the loaded style. Read [the Mapbox Style
* Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)
* for more informations.
*
* @param {string} [source.accessToken] - Mapbox access token
* @constructor
*/
constructor(source) {
Expand All @@ -52,9 +65,12 @@ class VectorTilesSource extends TMSSource {
this.styles = {};
let promise;

this.accessToken = source.accessToken;

if (source.style) {
if (typeof source.style == 'string') {
promise = Fetcher.json(source.style, this.networkOptions);
const styleUrl = urlParser.normalizeStyleURL(source.style, this.accessToken);
promise = Fetcher.json(styleUrl, this.networkOptions);
} else {
promise = Promise.resolve(source.style);
}
Expand All @@ -66,9 +82,11 @@ class VectorTilesSource extends TMSSource {
this.jsonStyle = style;
const baseurl = source.sprite || style.sprite;
if (baseurl) {
return Fetcher.json(`${baseurl}.json`, this.networkOptions).then((sprites) => {
const spriteUrl = urlParser.normalizeSpriteURL(baseurl, '', '.json', this.accessToken);
return Fetcher.json(spriteUrl, this.networkOptions).then((sprites) => {
this.sprites = sprites;
return Fetcher.texture(`${baseurl}.png`, this.networkOptions).then((texture) => {
const imgUrl = urlParser.normalizeSpriteURL(baseurl, '', '.png', this.accessToken);
return Fetcher.texture(imgUrl, this.networkOptions).then((texture) => {
this.sprites.img = texture.image;
return style;
});
Expand Down Expand Up @@ -132,7 +150,8 @@ class VectorTilesSource extends TMSSource {

if (this.url == '.') {
if (os.url) {
return Fetcher.json(os.url, this.networkOptions).then((tileJSON) => {
const urlSource = urlParser.normalizeSourceURL(os.url, this.accessToken);
return Fetcher.json(urlSource, this.networkOptions).then((tileJSON) => {
if (tileJSON.tiles[0]) {
this.url = toTMSUrl(tileJSON.tiles[0]);
}
Expand Down
24 changes: 24 additions & 0 deletions test/unit/vectortiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import HttpsProxyAgent from 'https-proxy-agent';
import VectorTileParser, { getStyle } from 'Parser/VectorTileParser';
import VectorTilesSource from 'Source/VectorTilesSource';
import Extent from 'Core/Geographic/Extent';
import urlParser from 'Parser/MapBoxUrlParser';

describe('Vector tiles', function () {
// this PBF file comes from https://github.com/mapbox/vector-tile-js
Expand Down Expand Up @@ -215,5 +216,28 @@ describe('Vector tiles', function () {
done();
});
});

it('Vector tile source mapbox url', () => {
const accessToken = 'pk.xxxxx';
const baseurl = 'mapbox://styles/mapbox/outdoors-v11';

const styleUrl = urlParser.normalizeStyleURL(baseurl, accessToken);
assert.ok(styleUrl.startsWith('https://api.mapbox.com'));
assert.ok(styleUrl.endsWith(accessToken));

const spriteUrl = urlParser.normalizeSpriteURL(baseurl, '', '.json', accessToken);
assert.ok(spriteUrl.startsWith('https'));
assert.ok(spriteUrl.endsWith(accessToken));
assert.ok(spriteUrl.includes('sprite.json'));

const imgUrl = urlParser.normalizeSpriteURL(baseurl, '', '.png', accessToken);
assert.ok(imgUrl.includes('sprite.png'));

const url = 'mapbox://mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2';
const urlSource = urlParser.normalizeSourceURL(url, accessToken);
assert.ok(urlSource.startsWith('https'));
assert.ok(urlSource.endsWith(accessToken));
assert.ok(urlSource.includes('.json'));
});
});
});

0 comments on commit b6dd383

Please sign in to comment.