├── .npmignore ├── .gitignore ├── debug ├── src │ ├── main.ts │ └── App.vue ├── tsconfig.json ├── vite.config.ts ├── .gitignore ├── index.html ├── README.md ├── tsconfig.app.json ├── package.json └── tsconfig.node.json ├── CHANGES.md ├── src ├── index.d.ts └── index.js ├── package.json ├── LICENSE └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | debug/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | debug/node_modules 3 | debug/.vscode 4 | package-lock.json 5 | pnpm-lock.yaml 6 | -------------------------------------------------------------------------------- /debug/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.0.1 2 | - Initial release 3 | 4 | ## 0.0.3 5 | - Add improved sprite support 6 | - add v12 mapbox style support 7 | -------------------------------------------------------------------------------- /debug/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /debug/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vite.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | }) 8 | -------------------------------------------------------------------------------- /debug/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /debug/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ResourceType } from 'maplibre-gl'; 2 | import { StyleSpecification, RequestParameters } from 'maplibre-gl' 3 | 4 | export function isMapboxURL(url: string): boolean; 5 | 6 | export function transformMapboxUrl(url: string, resourceType?: ResourceType, accessToken?: string): RequestParameters 7 | 8 | export function transformMapboxStyle(_previousStyle: StyleSpecification, nextStyle: StyleSpecification): StyleSpecification 9 | -------------------------------------------------------------------------------- /debug/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 41 | 42 | 48 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export function isMapboxURL(url) { 2 | return url.indexOf('mapbox:') === 0; 3 | } 4 | 5 | export function transformMapboxStyle (_previousStyle, nextStyle) { 6 | if (nextStyle.projection && nextStyle.projection.name) { 7 | delete nextStyle.projection.name; 8 | } 9 | return nextStyle; 10 | } 11 | 12 | export function transformMapboxUrl (url, resourceType, accessToken) { 13 | if (url.indexOf('/styles/') > -1 && url.indexOf('/sprite') === -1) return {url: normalizeStyleURL(url, accessToken)} 14 | if (url.indexOf('/sprites/') > -1) return {url: normalizeSpriteURL(url, '', '.json', accessToken)} 15 | if (url.indexOf('/fonts/') > -1) return {url: normalizeGlyphsURL(url, accessToken)} 16 | if (url.indexOf('/v4/') > -1) return {url: normalizeSourceURL(url, accessToken)} 17 | if (resourceType && resourceType === 'Source') return {url: normalizeSourceURL(url, accessToken)} 18 | } 19 | 20 | function parseUrl(url) { 21 | const urlRe = /^(\w+):\/\/([^/?]*)(\/[^?]+)?\??(.+)?/; 22 | const parts = url.match(urlRe); 23 | if (!parts) { 24 | throw new Error('Unable to parse URL object'); 25 | } 26 | return { 27 | protocol: parts[1], 28 | authority: parts[2], 29 | path: parts[3] || '/', 30 | params: parts[4] ? parts[4].split('&') : [] 31 | }; 32 | } 33 | 34 | function formatUrl(urlObject, accessToken) { 35 | const apiUrlObject = parseUrl("https://api.mapbox.com"); 36 | urlObject.protocol = apiUrlObject.protocol; 37 | urlObject.authority = apiUrlObject.authority; 38 | urlObject.params.push(`access_token=${accessToken}`); 39 | const params = urlObject.params.length ? `?${urlObject.params.join('&')}` : ''; 40 | return `${urlObject.protocol}://${urlObject.authority}${urlObject.path}${params}`; 41 | } 42 | 43 | function normalizeStyleURL(url, accessToken) { 44 | const urlObject = parseUrl(url); 45 | urlObject.path = `/styles/v1${urlObject.path}`; 46 | return formatUrl(urlObject, accessToken); 47 | } 48 | 49 | function normalizeGlyphsURL(url, accessToken) { 50 | const urlObject = parseUrl(url); 51 | urlObject.path = `/fonts/v1${urlObject.path}`; 52 | return formatUrl(urlObject, accessToken); 53 | } 54 | 55 | function normalizeSourceURL(url, accessToken) { 56 | const urlObject = parseUrl(url); 57 | urlObject.path = `/v4/${urlObject.authority}.json`; 58 | urlObject.params.push('secure'); 59 | return formatUrl(urlObject, accessToken); 60 | } 61 | 62 | /** 63 | * Normalizes a sprite URL. 64 | * 65 | * @param {string} url - The original sprite URL. 66 | * @param {string} _format - The format (not used in the function). 67 | * @param {string} _extension - The extension (not used in the function). 68 | * @param {string} accessToken - The access token. 69 | * 70 | * @returns {string} - The normalized URL. 71 | * 72 | * @throws {Error} Throws an error if the URL cannot be normalized. 73 | */ 74 | function normalizeSpriteURL(url, _format, _extension, accessToken) { 75 | const urlObject = parseUrl(url); 76 | let path = urlObject.path.split('.'); 77 | let properPath = path[0] 78 | const extension = path[1] || 'json'; // <- this only line is changed 79 | let format = '' 80 | 81 | if (properPath.indexOf('@2x')) { 82 | properPath = properPath.split('@2x')[0]; 83 | format = '@2x' 84 | } 85 | urlObject.path = `/styles/v1${properPath}/sprite${format}.${extension}`; 86 | return formatUrl(urlObject, accessToken); 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This library provides a request transforming function enabling the consumption of MapboxGL Styles in MapLibreGL. 2 | 3 | By default Mapbox styles are referenced by, and include others to mapbox URLs such as `mapbox://styles/mapbox/satellite-streets-v11`, however MapLibreGL, since version 2, does not know how to handle these references. This library parsers the mapbox URL and converts into a valid http urls usable by MapLibreGL. 4 | 5 | Also compatible with react-map-gl. 6 | 7 | ## Install 8 | ```` 9 | npm install maplibregl-mapbox-request-transformer --save 10 | ```` 11 | 12 | ## Usage 13 | ### For v12 mapbox styles 14 | For v12 styles you can either use the built in style transformer 15 | ```` 16 | import { 17 | isMapboxURL, 18 | transformMapboxUrl, 19 | transformMapboxStyle 20 | } from 'maplibregl-mapbox-request-transformer' 21 | 22 | const mapboxKey = 'pk.123' 23 | 24 | const transformRequest = (url: string, resourceType: string) => { 25 | if (isMapboxURL(url)) { 26 | return transformMapboxUrl(url, resourceType, mapboxKey) 27 | } 28 | return {url} 29 | } 30 | 31 | var map = new maplibregl.Map({ 32 | container: 'map', 33 | center: [-122.420679, 37.772537], 34 | zoom: 13, 35 | transformRequest 36 | }); 37 | 38 | // For V12 Styles you'll also need to add 39 | map.setStyle('mapbox://styles/mapbox/streets-v12', { 40 | transformStyle: transformMapboxStyle 41 | }) 42 | ```` 43 | Or pass you can pass in `validateStyle: false` to the map options 44 | ```` 45 | import { 46 | isMapboxURL, 47 | transformMapboxUrl 48 | } from 'maplibregl-mapbox-request-transformer' 49 | 50 | const mapboxKey = 'pk.123' 51 | 52 | const transformRequest = (url: string, resourceType: string) => { 53 | if (isMapboxURL(url)) { 54 | return transformMapboxUrl(url, resourceType, mapboxKey) 55 | } 56 | return {url} 57 | } 58 | 59 | var map = new maplibregl.Map({ 60 | container: 'map', 61 | center: [-122.420679, 37.772537], 62 | zoom: 13, 63 | style: 'mapbox://styles/mapbox/streets-v12' 64 | transformRequest, 65 | validateStyle: false, 66 | }); 67 | 68 | ```` 69 | 70 | ### For < v12 mapbox styles 71 | ```` 72 | import { isMapboxURL, transformMapboxUrl } from 'maplibregl-mapbox-request-transformer' 73 | 74 | const mapboxKey = 'pk.123' 75 | 76 | const transformRequest = (url: string, resourceType: string) => { 77 | if (isMapboxURL(url)) { 78 | return transformMapboxUrl(url, resourceType, mapboxKey) 79 | } 80 | return {url} 81 | } 82 | 83 | var map = new maplibregl.Map({ 84 | container: 'map', 85 | center: [-122.420679, 37.772537], 86 | zoom: 13, 87 | style: 'mapbox://styles/mapbox/streets-v11' 88 | transformRequest 89 | }); 90 | 91 | ```` 92 | 93 | ## Notes on Mapbox Pricing 94 | When upgrading from MapLibre v1 to >= v2 or react-map-gl, and using this transformer, be advised that Mapbox bills tile requests using their Vector Tiles API requests. A standard Mapbox GL JS application is billed using the Map Loads API, which includes unlimited Vector Tiles API requests ([see pricing docs](https://docs.mapbox.com/mapbox-gl-js/guides/pricing/)). This billing difference could make hosting an often-visited map more expensive. As of Feb 2023 Mapbox offers 200,000 monthly tile requests on their free tier (see their [pricing page](https://www.mapbox.com/pricing#vector-tiles-api) for current details). 95 | 96 | ## Acknowledgements 97 | This code was mostly taken and adpted from [here](https://github.com/maplibre/maplibre-gl-js/blob/04ff47d53ec16e17b92475fe9028c1477f6df02f/src/util/mapbox.ts). 98 | --------------------------------------------------------------------------------