Spaces:
Sleeping
Sleeping
| import URL from 'url'; | |
| import {eachSource, eachLayer, eachProperty} from '../visit'; | |
| function eachLayout(layer, callback) { | |
| for (const k in layer) { | |
| if (k.indexOf('layout') === 0) { | |
| callback(layer[k], k); | |
| } | |
| } | |
| } | |
| function eachPaint(layer, callback) { | |
| for (const k in layer) { | |
| if (k.indexOf('paint') === 0) { | |
| callback(layer[k], k); | |
| } | |
| } | |
| } | |
| function resolveConstant(style, value) { | |
| if (typeof value === 'string' && value[0] === '@') { | |
| return resolveConstant(style, style.constants[value]); | |
| } else { | |
| return value; | |
| } | |
| } | |
| function isFunction(value) { | |
| return Array.isArray(value.stops); | |
| } | |
| function renameProperty(obj, from, to) { | |
| obj[to] = obj[from]; delete obj[from]; | |
| } | |
| export default function(style) { | |
| style.version = 8; | |
| // Rename properties, reverse coordinates in source and layers | |
| eachSource(style, (source) => { | |
| if (source.type === 'video' && source.url !== undefined) { | |
| renameProperty(source, 'url', 'urls'); | |
| } | |
| if (source.type === 'video') { | |
| source.coordinates.forEach((coord) => { | |
| return coord.reverse(); | |
| }); | |
| } | |
| }); | |
| eachLayer(style, (layer) => { | |
| eachLayout(layer, (layout) => { | |
| if (layout['symbol-min-distance'] !== undefined) { | |
| renameProperty(layout, 'symbol-min-distance', 'symbol-spacing'); | |
| } | |
| }); | |
| eachPaint(layer, (paint) => { | |
| if (paint['background-image'] !== undefined) { | |
| renameProperty(paint, 'background-image', 'background-pattern'); | |
| } | |
| if (paint['line-image'] !== undefined) { | |
| renameProperty(paint, 'line-image', 'line-pattern'); | |
| } | |
| if (paint['fill-image'] !== undefined) { | |
| renameProperty(paint, 'fill-image', 'fill-pattern'); | |
| } | |
| }); | |
| }); | |
| // Inline Constants | |
| eachProperty(style, {paint: true, layout: true}, (property) => { | |
| const value = resolveConstant(style, property.value); | |
| if (isFunction(value)) { | |
| value.stops.forEach((stop) => { | |
| stop[1] = resolveConstant(style, stop[1]); | |
| }); | |
| } | |
| property.set(value); | |
| }); | |
| delete style.constants; | |
| eachLayer(style, (layer) => { | |
| // get rid of text-max-size, icon-max-size | |
| // turn text-size, icon-size into layout properties | |
| // https://github.com/mapbox/mapbox-gl-style-spec/issues/255 | |
| eachLayout(layer, (layout) => { | |
| delete layout['text-max-size']; | |
| delete layout['icon-max-size']; | |
| }); | |
| eachPaint(layer, (paint) => { | |
| if (paint['text-size']) { | |
| if (!layer.layout) layer.layout = {}; | |
| layer.layout['text-size'] = paint['text-size']; | |
| delete paint['text-size']; | |
| } | |
| if (paint['icon-size']) { | |
| if (!layer.layout) layer.layout = {}; | |
| layer.layout['icon-size'] = paint['icon-size']; | |
| delete paint['icon-size']; | |
| } | |
| }); | |
| }); | |
| function migrateFontstackURL(input) { | |
| const inputParsed = URL.parse(input); | |
| const inputPathnameParts = inputParsed.pathname.split('/'); | |
| if (inputParsed.protocol !== 'mapbox:') { | |
| return input; | |
| } else if (inputParsed.hostname === 'fontstack') { | |
| assert(decodeURI(inputParsed.pathname) === '/{fontstack}/{range}.pbf'); | |
| return 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf'; | |
| } else if (inputParsed.hostname === 'fonts') { | |
| assert(inputPathnameParts[1] === 'v1'); | |
| assert(decodeURI(inputPathnameParts[3]) === '{fontstack}'); | |
| assert(decodeURI(inputPathnameParts[4]) === '{range}.pbf'); | |
| return `mapbox://fonts/${inputPathnameParts[2]}/{fontstack}/{range}.pbf`; | |
| } else { | |
| assert(false); | |
| } | |
| function assert(predicate) { | |
| if (!predicate) { | |
| throw new Error(`Invalid font url: "${input}"`); | |
| } | |
| } | |
| } | |
| if (style.glyphs) { | |
| style.glyphs = migrateFontstackURL(style.glyphs); | |
| } | |
| function migrateFontStack(font) { | |
| function splitAndTrim(string) { | |
| return string.split(',').map((s) => { | |
| return s.trim(); | |
| }); | |
| } | |
| if (Array.isArray(font)) { | |
| // Assume it's a previously migrated font-array. | |
| return font; | |
| } else if (typeof font === 'string') { | |
| return splitAndTrim(font); | |
| } else if (typeof font === 'object') { | |
| font.stops.forEach((stop) => { | |
| stop[1] = splitAndTrim(stop[1]); | |
| }); | |
| return font; | |
| } else { | |
| throw new Error("unexpected font value"); | |
| } | |
| } | |
| eachLayer(style, (layer) => { | |
| eachLayout(layer, (layout) => { | |
| if (layout['text-font']) { | |
| layout['text-font'] = migrateFontStack(layout['text-font']); | |
| } | |
| }); | |
| }); | |
| // Reverse order of symbol layers. This is an imperfect migration. | |
| // | |
| // The order of a symbol layer in the layers list affects two things: | |
| // - how it is drawn relative to other layers (like oneway arrows below bridges) | |
| // - the placement priority compared to other layers | |
| // | |
| // It's impossible to reverse the placement priority without breaking the draw order | |
| // in some cases. This migration only reverses the order of symbol layers that | |
| // are above all other types of layers. | |
| // | |
| // Symbol layers that are at the top of the map preserve their priority. | |
| // Symbol layers that are below another type (line, fill) of layer preserve their draw order. | |
| let firstSymbolLayer = 0; | |
| for (let i = style.layers.length - 1; i >= 0; i--) { | |
| const layer = style.layers[i]; | |
| if (layer.type !== 'symbol') { | |
| firstSymbolLayer = i + 1; | |
| break; | |
| } | |
| } | |
| const symbolLayers = style.layers.splice(firstSymbolLayer); | |
| symbolLayers.reverse(); | |
| style.layers = style.layers.concat(symbolLayers); | |
| return style; | |
| } | |