Merge pull request #13552 from ahocevar/ol-mapbox-style-8
Updates for ol-mapbox-style v8
This commit is contained in:
@@ -5,9 +5,9 @@ import TileQueue, {
|
||||
import VectorTileLayer from '../src/ol/layer/VectorTile.js';
|
||||
import VectorTileSource from '../src/ol/source/VectorTile.js';
|
||||
import stringify from 'json-stringify-safe';
|
||||
import styleFunction from 'ol-mapbox-style/dist/stylefunction.js';
|
||||
import {get} from '../src/ol/proj.js';
|
||||
import {inView} from '../src/ol/layer/Layer.js';
|
||||
import {stylefunction} from 'ol-mapbox-style';
|
||||
|
||||
/** @type {any} */
|
||||
const worker = self;
|
||||
@@ -95,7 +95,7 @@ function loadStyles() {
|
||||
};
|
||||
rendererTransform = transform;
|
||||
};
|
||||
styleFunction(
|
||||
stylefunction(
|
||||
layer,
|
||||
styleJson,
|
||||
bucket.layers,
|
||||
|
||||
@@ -1,55 +1,36 @@
|
||||
import MVT from '../src/ol/format/MVT.js';
|
||||
import TileGrid from '../src/ol/tilegrid/TileGrid.js';
|
||||
import VectorTileLayer from '../src/ol/layer/VectorTile.js';
|
||||
import VectorTileSource from '../src/ol/source/VectorTile.js';
|
||||
import View from '../src/ol/View.js';
|
||||
|
||||
import olms from 'ol-mapbox-style';
|
||||
import {defaultResolutions} from 'ol-mapbox-style/dist/util.js';
|
||||
import {Map, View} from '../src/ol/index.js';
|
||||
import {applyBackground, applyStyle} from 'ol-mapbox-style';
|
||||
import {createXYZ} from '../src/ol/tilegrid.js';
|
||||
|
||||
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||
const url = 'https://api.maptiler.com/maps/basic-4326/style.json?key=' + key;
|
||||
|
||||
// Match the server resolutions
|
||||
const maxResolution = 360 / 512;
|
||||
defaultResolutions.length = 14;
|
||||
for (let i = 0; i < 14; ++i) {
|
||||
defaultResolutions[i] = maxResolution / Math.pow(2, i + 1);
|
||||
}
|
||||
|
||||
olms(
|
||||
'map',
|
||||
'https://api.maptiler.com/maps/basic-4326/style.json?key=' + key
|
||||
).then(function (map) {
|
||||
// Custom tile grid for the EPSG:4326 projection
|
||||
const tileGrid = new TileGrid({
|
||||
const tileGrid = createXYZ({
|
||||
extent: [-180, -90, 180, 90],
|
||||
tileSize: 512,
|
||||
resolutions: defaultResolutions,
|
||||
});
|
||||
|
||||
const mapboxStyle = map.get('mapbox-style');
|
||||
|
||||
// Replace the source with a EPSG:4326 projection source for each vector tile layer
|
||||
map.getLayers().forEach(function (layer) {
|
||||
const mapboxSource = layer.get('mapbox-source');
|
||||
if (mapboxSource && mapboxStyle.sources[mapboxSource].type === 'vector') {
|
||||
const source = layer.getSource();
|
||||
layer.setSource(
|
||||
new VectorTileSource({
|
||||
format: new MVT(),
|
||||
projection: 'EPSG:4326',
|
||||
urls: source.getUrls(),
|
||||
tileGrid: tileGrid,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Configure the map with a view with EPSG:4326 projection
|
||||
map.setView(
|
||||
new View({
|
||||
projection: 'EPSG:4326',
|
||||
zoom: mapboxStyle.zoom,
|
||||
center: mapboxStyle.center,
|
||||
})
|
||||
);
|
||||
maxResolution: 180 / 512,
|
||||
maxZoom: 13,
|
||||
});
|
||||
|
||||
const layer = new VectorTileLayer({
|
||||
declutter: true,
|
||||
source: new VectorTileSource({
|
||||
projection: 'EPSG:4326',
|
||||
tileGrid: tileGrid,
|
||||
}),
|
||||
});
|
||||
applyStyle(layer, url, '', {resolutions: tileGrid.getResolutions()});
|
||||
applyBackground(layer, url);
|
||||
|
||||
const map = new Map({
|
||||
target: 'map',
|
||||
layers: [layer],
|
||||
view: new View({
|
||||
projection: 'EPSG:4326',
|
||||
zoom: 0,
|
||||
center: [0, 30],
|
||||
}),
|
||||
});
|
||||
|
||||
121
package-lock.json
generated
121
package-lock.json
generated
@@ -10,7 +10,7 @@
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"geotiff": "^2.0.2",
|
||||
"ol-mapbox-style": "^7.1.1",
|
||||
"ol-mapbox-style": "^8.0.5",
|
||||
"pbf": "3.2.1",
|
||||
"rbush": "^3.0.1"
|
||||
},
|
||||
@@ -62,6 +62,7 @@
|
||||
"serve-static": "^1.14.0",
|
||||
"shx": "^0.3.2",
|
||||
"sinon": "^13.0.0",
|
||||
"source-map-loader": "^3.0.1",
|
||||
"threads": "^1.6.5",
|
||||
"typescript": "4.6.3",
|
||||
"walk": "^2.3.9",
|
||||
@@ -1785,9 +1786,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/mapbox-gl-style-spec": {
|
||||
"version": "13.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.20.1.tgz",
|
||||
"integrity": "sha512-xVCJ3IbKoPwcPrxxtGAxUqHEVxXi1hnJtLIFqgkuZfnzj0KeRbk3dZlDr/KNo1/doJjIoFgPFUO/HMOT+wXGPA==",
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.24.0.tgz",
|
||||
"integrity": "sha512-9yhRSqnKX+59MrG647x2pACfR2Ewk8Ii5X75Ag8oToEKZU+PSY0+r10EirIVCTDwuHPzp/VuuN3j6n+Hv/gGpQ==",
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
@@ -1799,10 +1800,10 @@
|
||||
"sort-object": "^0.3.2"
|
||||
},
|
||||
"bin": {
|
||||
"gl-style-composite": "bin/gl-style-composite",
|
||||
"gl-style-format": "bin/gl-style-format",
|
||||
"gl-style-migrate": "bin/gl-style-migrate",
|
||||
"gl-style-validate": "bin/gl-style-validate"
|
||||
"gl-style-composite": "bin/gl-style-composite.js",
|
||||
"gl-style-format": "bin/gl-style-format.js",
|
||||
"gl-style-migrate": "bin/gl-style-migrate.js",
|
||||
"gl-style-validate": "bin/gl-style-validate.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/point-geometry": {
|
||||
@@ -2475,6 +2476,12 @@
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -7451,11 +7458,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ol-mapbox-style": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-7.1.1.tgz",
|
||||
"integrity": "sha512-GLTEYiH/Ec9Zn1eS4S/zXyR2sierVrUc+OLVP8Ra0FRyqRhoYbXdko0b7OIeSHWdtJfHssWYefDOGxfTRUUZ/A==",
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-8.0.5.tgz",
|
||||
"integrity": "sha512-krDZv3pBVuU52Hmk6b2oXS6z8TVTAteR6S5JmZNyNTVG+ghl8tVeNILr57BRn0ACiC0T5hF6ZPcK/Cn94XaMqg==",
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.20.1",
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.23.1",
|
||||
"mapbox-to-css-font": "^2.4.1",
|
||||
"webfont-matcher": "^1.1.0"
|
||||
}
|
||||
@@ -9066,6 +9073,48 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-loader": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz",
|
||||
"integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"abab": "^2.0.5",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-loader/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
@@ -11802,9 +11851,9 @@
|
||||
"integrity": "sha1-zlblOfg1UrWNENZy6k1vya3HsjQ="
|
||||
},
|
||||
"@mapbox/mapbox-gl-style-spec": {
|
||||
"version": "13.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.20.1.tgz",
|
||||
"integrity": "sha512-xVCJ3IbKoPwcPrxxtGAxUqHEVxXi1hnJtLIFqgkuZfnzj0KeRbk3dZlDr/KNo1/doJjIoFgPFUO/HMOT+wXGPA==",
|
||||
"version": "13.24.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.24.0.tgz",
|
||||
"integrity": "sha512-9yhRSqnKX+59MrG647x2pACfR2Ewk8Ii5X75Ag8oToEKZU+PSY0+r10EirIVCTDwuHPzp/VuuN3j6n+Hv/gGpQ==",
|
||||
"requires": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/point-geometry": "^0.1.0",
|
||||
@@ -12429,6 +12478,12 @@
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"abab": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -16194,11 +16249,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"ol-mapbox-style": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-7.1.1.tgz",
|
||||
"integrity": "sha512-GLTEYiH/Ec9Zn1eS4S/zXyR2sierVrUc+OLVP8Ra0FRyqRhoYbXdko0b7OIeSHWdtJfHssWYefDOGxfTRUUZ/A==",
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-8.0.5.tgz",
|
||||
"integrity": "sha512-krDZv3pBVuU52Hmk6b2oXS6z8TVTAteR6S5JmZNyNTVG+ghl8tVeNILr57BRn0ACiC0T5hF6ZPcK/Cn94XaMqg==",
|
||||
"requires": {
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.20.1",
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.23.1",
|
||||
"mapbox-to-css-font": "^2.4.1",
|
||||
"webfont-matcher": "^1.1.0"
|
||||
}
|
||||
@@ -17429,6 +17484,34 @@
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-loader": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz",
|
||||
"integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"abab": "^2.0.5",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"geotiff": "^2.0.2",
|
||||
"ol-mapbox-style": "^7.1.1",
|
||||
"ol-mapbox-style": "^8.0.5",
|
||||
"pbf": "3.2.1",
|
||||
"rbush": "^3.0.1"
|
||||
},
|
||||
@@ -98,6 +98,7 @@
|
||||
"serve-static": "^1.14.0",
|
||||
"shx": "^0.3.2",
|
||||
"sinon": "^13.0.0",
|
||||
"source-map-loader": "^3.0.1",
|
||||
"threads": "^1.6.5",
|
||||
"typescript": "4.6.3",
|
||||
"walk": "^2.3.9",
|
||||
|
||||
@@ -7,110 +7,7 @@ import MVT from '../format/MVT.js';
|
||||
import SourceState from '../source/State.js';
|
||||
import VectorTileLayer from '../layer/VectorTile.js';
|
||||
import VectorTileSource from '../source/VectorTile.js';
|
||||
import {applyBackground, applyStyle, setupVectorSource} from 'ol-mapbox-style';
|
||||
|
||||
const mapboxBaseUrl = 'https://api.mapbox.com';
|
||||
|
||||
/**
|
||||
* Gets the path from a mapbox:// URL.
|
||||
* @param {string} url The Mapbox URL.
|
||||
* @return {string} The path.
|
||||
* @private
|
||||
*/
|
||||
export function getMapboxPath(url) {
|
||||
const startsWith = 'mapbox://';
|
||||
if (url.indexOf(startsWith) !== 0) {
|
||||
return '';
|
||||
}
|
||||
return url.slice(startsWith.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns mapbox:// sprite URLs into resolvable URLs.
|
||||
* @param {string} url The sprite URL.
|
||||
* @param {string} token The access token.
|
||||
* @param {string} styleUrl The style URL.
|
||||
* @return {string} A resolvable URL.
|
||||
* @private
|
||||
*/
|
||||
export function normalizeSpriteUrl(url, token, styleUrl) {
|
||||
const mapboxPath = getMapboxPath(url);
|
||||
if (!mapboxPath) {
|
||||
return decodeURI(new URL(url, styleUrl).href);
|
||||
}
|
||||
const startsWith = 'sprites/';
|
||||
if (mapboxPath.indexOf(startsWith) !== 0) {
|
||||
throw new Error(`unexpected sprites url: ${url}`);
|
||||
}
|
||||
const sprite = mapboxPath.slice(startsWith.length);
|
||||
|
||||
return `${mapboxBaseUrl}/styles/v1/${sprite}/sprite?access_token=${token}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns mapbox:// glyphs URLs into resolvable URLs.
|
||||
* @param {string} url The glyphs URL.
|
||||
* @param {string} token The access token.
|
||||
* @param {string} styleUrl The style URL.
|
||||
* @return {string} A resolvable URL.
|
||||
* @private
|
||||
*/
|
||||
export function normalizeGlyphsUrl(url, token, styleUrl) {
|
||||
const mapboxPath = getMapboxPath(url);
|
||||
if (!mapboxPath) {
|
||||
return decodeURI(new URL(url, styleUrl).href);
|
||||
}
|
||||
const startsWith = 'fonts/';
|
||||
if (mapboxPath.indexOf(startsWith) !== 0) {
|
||||
throw new Error(`unexpected fonts url: ${url}`);
|
||||
}
|
||||
const font = mapboxPath.slice(startsWith.length);
|
||||
|
||||
return `${mapboxBaseUrl}/fonts/v1/${font}/0-255.pbf?access_token=${token}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns mapbox:// style URLs into resolvable URLs.
|
||||
* @param {string} url The style URL.
|
||||
* @param {string} token The access token.
|
||||
* @return {string} A resolvable URL.
|
||||
* @private
|
||||
*/
|
||||
export function normalizeStyleUrl(url, token) {
|
||||
const mapboxPath = getMapboxPath(url);
|
||||
if (!mapboxPath) {
|
||||
return decodeURI(new URL(url, location.href).href);
|
||||
}
|
||||
const startsWith = 'styles/';
|
||||
if (mapboxPath.indexOf(startsWith) !== 0) {
|
||||
throw new Error(`unexpected style url: ${url}`);
|
||||
}
|
||||
const style = mapboxPath.slice(startsWith.length);
|
||||
|
||||
return `${mapboxBaseUrl}/styles/v1/${style}?&access_token=${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.
|
||||
* @param {string} styleUrl The style URL.
|
||||
* @return {string} A vector tile template.
|
||||
* @private
|
||||
*/
|
||||
export function normalizeSourceUrl(url, token, tokenParam, styleUrl) {
|
||||
const urlObject = new URL(url, styleUrl);
|
||||
const mapboxPath = getMapboxPath(url);
|
||||
if (!mapboxPath) {
|
||||
if (!token) {
|
||||
return decodeURI(urlObject.href);
|
||||
}
|
||||
urlObject.searchParams.set(tokenParam, token);
|
||||
return decodeURI(urlObject.href);
|
||||
}
|
||||
return `https://{a-d}.tiles.mapbox.com/v4/${mapboxPath}/{z}/{x}/{y}.vector.pbf?access_token=${token}`;
|
||||
}
|
||||
import {applyBackground, applyStyle} from 'ol-mapbox-style';
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -130,38 +27,6 @@ class ErrorEvent extends BaseEvent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} StyleObject
|
||||
* @property {Object<string, SourceObject>} sources The style sources.
|
||||
* @property {string} sprite The sprite URL.
|
||||
* @property {string} glyphs The glyphs URL.
|
||||
* @property {Array<LayerObject>} layers The style layers.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SourceObject
|
||||
* @property {string} url The source URL.
|
||||
* @property {SourceType} type The source type.
|
||||
* @property {Array<string>} [tiles] TileJSON tiles.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Mapbox source type.
|
||||
* @enum {string}
|
||||
*/
|
||||
const SourceType = {
|
||||
VECTOR: 'vector',
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} LayerObject
|
||||
* @property {string} id The layer id.
|
||||
* @property {string} type The layer type.
|
||||
* @property {string} source The source id.
|
||||
* @property {Object} layout The layout.
|
||||
* @property {Object} paint The paint.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {string} styleUrl The URL of the Mapbox style object to use for this layer. For a
|
||||
@@ -306,211 +171,26 @@ class MapboxVectorLayer extends VectorTileLayer {
|
||||
properties: options.properties,
|
||||
});
|
||||
|
||||
this.setMaxResolutionFromTileGrid_ =
|
||||
options.maxResolution === undefined && options.minZoom === undefined;
|
||||
|
||||
this.sourceId = options.source;
|
||||
this.layers = options.layers;
|
||||
|
||||
if (options.accessToken) {
|
||||
this.accessToken = options.accessToken;
|
||||
} else {
|
||||
const url = new URL(options.styleUrl, location.href);
|
||||
// The last search parameter is the access token
|
||||
url.searchParams.forEach((value, key) => {
|
||||
this.accessToken = value;
|
||||
this.accessTokenParam_ = key;
|
||||
});
|
||||
}
|
||||
this.fetchStyle(options.styleUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the style object.
|
||||
* @param {string} styleUrl The URL of the style to load.
|
||||
* @protected
|
||||
*/
|
||||
fetchStyle(styleUrl) {
|
||||
const url = normalizeStyleUrl(styleUrl, this.accessToken);
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`unexpected response when fetching style: ${response.status}`
|
||||
);
|
||||
}
|
||||
return response.json();
|
||||
const url = options.styleUrl;
|
||||
applyStyle(this, url, options.layers || options.source, {
|
||||
accessToken: this.accessToken,
|
||||
})
|
||||
.then((style) => {
|
||||
this.onStyleLoad(style, url.startsWith('data:') ? location.href : url);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.handleError(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the loaded style object.
|
||||
* @param {StyleObject} style The loaded style.
|
||||
* @param {string} styleUrl The URL of the style.
|
||||
* @protected
|
||||
*/
|
||||
onStyleLoad(style, styleUrl) {
|
||||
let sourceId;
|
||||
let sourceIdOrLayersList;
|
||||
if (this.layers) {
|
||||
// confirm all layers share the same source
|
||||
const lookup = {};
|
||||
for (let i = 0; i < style.layers.length; ++i) {
|
||||
const layer = style.layers[i];
|
||||
if (layer.source) {
|
||||
lookup[layer.id] = layer.source;
|
||||
}
|
||||
}
|
||||
let firstSource;
|
||||
for (let i = 0; i < this.layers.length; ++i) {
|
||||
const candidate = lookup[this.layers[i]];
|
||||
if (!candidate) {
|
||||
this.handleError(
|
||||
new Error(`could not find source for ${this.layers[i]}`)
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!firstSource) {
|
||||
firstSource = candidate;
|
||||
} else if (firstSource !== candidate) {
|
||||
this.handleError(
|
||||
new Error(
|
||||
`layers can only use a single source, found ${firstSource} and ${candidate}`
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
sourceId = firstSource;
|
||||
sourceIdOrLayersList = this.layers;
|
||||
} else {
|
||||
sourceId = this.sourceId;
|
||||
sourceIdOrLayersList = sourceId;
|
||||
}
|
||||
|
||||
if (!sourceIdOrLayersList) {
|
||||
// default to the first source in the style
|
||||
sourceId = Object.keys(style.sources)[0];
|
||||
sourceIdOrLayersList = sourceId;
|
||||
}
|
||||
|
||||
if (style.sprite) {
|
||||
style.sprite = normalizeSpriteUrl(
|
||||
style.sprite,
|
||||
this.accessToken,
|
||||
styleUrl
|
||||
);
|
||||
}
|
||||
|
||||
if (style.glyphs) {
|
||||
style.glyphs = normalizeGlyphsUrl(
|
||||
style.glyphs,
|
||||
this.accessToken,
|
||||
styleUrl
|
||||
);
|
||||
}
|
||||
|
||||
const styleSource = style.sources[sourceId];
|
||||
if (styleSource.type !== SourceType.VECTOR) {
|
||||
this.handleError(
|
||||
new Error(`only works for vector sources, found ${styleSource.type}`)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const source = this.getSource();
|
||||
if (styleSource.url && styleSource.url.indexOf('mapbox://') === 0) {
|
||||
// Tile source url, handle it directly
|
||||
source.setUrl(
|
||||
normalizeSourceUrl(
|
||||
styleSource.url,
|
||||
this.accessToken,
|
||||
this.accessTokenParam_,
|
||||
styleUrl
|
||||
)
|
||||
);
|
||||
applyStyle(this, style, sourceIdOrLayersList)
|
||||
.then(() => {
|
||||
this.configureSource(source, style);
|
||||
source.setState(SourceState.READY);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.handleError(error);
|
||||
});
|
||||
} else {
|
||||
// TileJSON url, let ol-mapbox-style handle it
|
||||
if (styleSource.tiles) {
|
||||
styleSource.tiles = styleSource.tiles.map((url) =>
|
||||
normalizeSourceUrl(
|
||||
url,
|
||||
this.accessToken,
|
||||
this.accessTokenParam_,
|
||||
styleUrl
|
||||
)
|
||||
);
|
||||
}
|
||||
setupVectorSource(
|
||||
styleSource,
|
||||
styleSource.url
|
||||
? normalizeSourceUrl(
|
||||
styleSource.url,
|
||||
this.accessToken,
|
||||
this.accessTokenParam_,
|
||||
styleUrl
|
||||
)
|
||||
: undefined
|
||||
).then((source) => {
|
||||
applyStyle(this, style, sourceIdOrLayersList)
|
||||
.then(() => {
|
||||
this.configureSource(source, style);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.configureSource(source, style);
|
||||
this.handleError(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies configuration from the provided source to this layer's source,
|
||||
* and reconfigures the loader to add a feature that renders the background,
|
||||
* if the style is configured with a background.
|
||||
* @param {import("../source/VectorTile.js").default} source The source to configure from.
|
||||
* @param {StyleObject} style The style to configure the background from.
|
||||
*/
|
||||
configureSource(source, style) {
|
||||
const targetSource = this.getSource();
|
||||
if (source !== targetSource) {
|
||||
targetSource.setAttributions(source.getAttributions());
|
||||
targetSource.setTileUrlFunction(source.getTileUrlFunction());
|
||||
targetSource.setTileLoadFunction(source.getTileLoadFunction());
|
||||
targetSource.tileGrid = source.tileGrid;
|
||||
}
|
||||
if (this.getBackground() === undefined) {
|
||||
applyBackground(this, style);
|
||||
}
|
||||
if (this.setMaxResolutionFromTileGrid_) {
|
||||
const tileGrid = targetSource.getTileGrid();
|
||||
this.setMaxResolution(tileGrid.getResolution(tileGrid.getMinZoom()));
|
||||
}
|
||||
targetSource.setState(SourceState.READY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle configuration or loading error.
|
||||
* @param {Error} error The error.
|
||||
* @protected
|
||||
*/
|
||||
handleError(error) {
|
||||
this.dispatchEvent(new ErrorEvent(error));
|
||||
const source = this.getSource();
|
||||
source.setState(SourceState.ERROR);
|
||||
});
|
||||
if (this.getBackground() === undefined) {
|
||||
applyBackground(this, options.styleUrl, {
|
||||
accessToken: this.accessToken,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,11 @@ module.exports = function (karma) {
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
enforce: 'pre',
|
||||
use: ['source-map-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: {
|
||||
|
||||
@@ -1,123 +1,8 @@
|
||||
import MapboxVectorLayer, {
|
||||
getMapboxPath,
|
||||
normalizeSourceUrl,
|
||||
normalizeSpriteUrl,
|
||||
normalizeStyleUrl,
|
||||
} from '../../../../../src/ol/layer/MapboxVector.js';
|
||||
import MapboxVectorLayer from '../../../../../src/ol/layer/MapboxVector.js';
|
||||
import {asString} from '../../../../../src/ol/color.js';
|
||||
import {unByKey} from '../../../../../src/ol/Observable.js';
|
||||
|
||||
describe('ol/layer/MapboxVector', () => {
|
||||
describe('getMapboxPath()', () => {
|
||||
const cases = [
|
||||
{
|
||||
url: 'mapbox://path/to/resource',
|
||||
expected: 'path/to/resource',
|
||||
},
|
||||
{
|
||||
url: 'mapbox://path/to/resource?query',
|
||||
expected: 'path/to/resource?query',
|
||||
},
|
||||
{
|
||||
url: 'https://example.com/resource',
|
||||
expected: '',
|
||||
},
|
||||
];
|
||||
|
||||
for (const c of cases) {
|
||||
it(`works for ${c.url}`, () => {
|
||||
expect(getMapboxPath(c.url)).to.be(c.expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('normalizeStyleUrl()', () => {
|
||||
const cases = [
|
||||
{
|
||||
url: 'mapbox://styles/mapbox/bright-v9',
|
||||
expected:
|
||||
'https://api.mapbox.com/styles/v1/mapbox/bright-v9?&access_token=test-token',
|
||||
},
|
||||
{
|
||||
url: 'https://example.com/style',
|
||||
expected: 'https://example.com/style',
|
||||
},
|
||||
];
|
||||
|
||||
const token = 'test-token';
|
||||
for (const c of cases) {
|
||||
it(`works for ${c.url}`, () => {
|
||||
expect(normalizeStyleUrl(c.url, token)).to.be(c.expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('normalizeSpriteUrl()', () => {
|
||||
const cases = [
|
||||
{
|
||||
url: 'mapbox://sprites/mapbox/bright-v9',
|
||||
expected:
|
||||
'https://api.mapbox.com/styles/v1/mapbox/bright-v9/sprite?access_token=test-token',
|
||||
},
|
||||
{
|
||||
url: 'https://example.com/sprite',
|
||||
expected: 'https://example.com/sprite',
|
||||
},
|
||||
{
|
||||
url: '../sprite',
|
||||
expected: 'https://example.com:8000/sprite',
|
||||
},
|
||||
{
|
||||
url: '/sprite',
|
||||
expected: 'https://example.com:8000/sprite',
|
||||
},
|
||||
{
|
||||
url: './sprite',
|
||||
expected: 'https://example.com:8000/mystyle/sprite',
|
||||
},
|
||||
];
|
||||
|
||||
const token = 'test-token';
|
||||
for (const c of cases) {
|
||||
it(`works for ${c.url}`, () => {
|
||||
expect(
|
||||
normalizeSpriteUrl(
|
||||
c.url,
|
||||
token,
|
||||
'https://example.com:8000/mystyle/style.json'
|
||||
)
|
||||
).to.be(c.expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('normalizeSourceUrl()', () => {
|
||||
const cases = [
|
||||
{
|
||||
url: 'mapbox://mapbox.mapbox-streets-v7',
|
||||
expected:
|
||||
'https://{a-d}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf?access_token=test-token',
|
||||
},
|
||||
{
|
||||
url: '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, tokenParam)).to.be(c.expected);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('TileJSON', function () {
|
||||
it('lets ol-mapbox-style handle TileJSON URLs', function (done) {
|
||||
const layer = new MapboxVectorLayer({
|
||||
@@ -136,6 +21,9 @@ describe('ol/layer/MapboxVector', () => {
|
||||
})
|
||||
),
|
||||
});
|
||||
layer.on('error', function (e) {
|
||||
done(e.error);
|
||||
});
|
||||
const source = layer.getSource();
|
||||
const key = source.on('change', function () {
|
||||
if (source.getState() === 'ready') {
|
||||
@@ -185,6 +73,9 @@ describe('ol/layer/MapboxVector', () => {
|
||||
const layer = new MapboxVectorLayer({
|
||||
styleUrl: styleUrl,
|
||||
});
|
||||
layer.on('error', function (e) {
|
||||
done(e.error);
|
||||
});
|
||||
const source = layer.getSource();
|
||||
source.on('change', function onchange() {
|
||||
if (source.getState() === 'ready') {
|
||||
@@ -312,20 +203,31 @@ describe('ol/layer/MapboxVector', () => {
|
||||
});
|
||||
|
||||
describe('Access token', function () {
|
||||
it('applies correct access token from access_token', function () {
|
||||
const layer = new MapboxVectorLayer({
|
||||
let originalFetch, fetchUrl;
|
||||
beforeEach(function () {
|
||||
originalFetch = fetch;
|
||||
window.fetch = function (url) {
|
||||
fetchUrl = url;
|
||||
return Promise.resolve();
|
||||
};
|
||||
});
|
||||
afterEach(function () {
|
||||
window.fetch = originalFetch;
|
||||
});
|
||||
it('applies correct access token', function () {
|
||||
new MapboxVectorLayer({
|
||||
styleUrl: 'mapbox://styles/mapbox/streets-v7',
|
||||
accessToken: '123',
|
||||
});
|
||||
expect(layer.accessToken).to.be('123');
|
||||
expect(layer.accessTokenParam_).to.be(undefined);
|
||||
expect(fetchUrl.url).to.be(
|
||||
'https://api.mapbox.com/styles/v1/mapbox/streets-v7?&access_token=123'
|
||||
);
|
||||
});
|
||||
it('applies correct access token from url', function () {
|
||||
const layer = new MapboxVectorLayer({
|
||||
new MapboxVectorLayer({
|
||||
styleUrl: 'foo?key=123',
|
||||
});
|
||||
expect(layer.accessToken).to.be('123');
|
||||
expect(layer.accessTokenParam_).to.be('key');
|
||||
expect(fetchUrl.url).to.be(`${location.origin}/foo?key=123`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user