ol-mapbox-style
Version:
Create OpenLayers layers or maps from Mapbox/MapLibre styles
143 lines (136 loc) • 3.71 kB
JavaScript
import {
Color,
CompoundExpression,
expressions,
} from '@maplibre/maplibre-gl-style-spec';
import {fromString} from 'ol/color.js';
/**
* @param {import('@maplibre/maplibre-gl-style-spec').EvaluationContext} ctx Context
* @param {Array<import('@maplibre/maplibre-gl-style-spec').Expression>} args r, g, b, alpha
* @return {Color} The parsed HSLA color.
*/
function hsla(ctx, args) {
const h = args[0].evaluate(ctx);
const s = args[1].evaluate(ctx);
const l = args[2].evaluate(ctx);
const alpha = args[3] ? args[3].evaluate(ctx) : 1;
return Color.parse(`hsla(${h}, ${s}%, ${l}%, ${alpha})`);
}
function rgbaToHsla(rgba) {
const r = rgba[0] / 255;
const g = rgba[1] / 255;
const b = rgba[2] / 255;
const a = rgba[3];
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
let h, s;
if (max === min) {
h = 0;
s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
default:
h = 0;
}
h /= 6;
}
return [h * 360, s * 100, l * 100, a];
}
export function wrapImageExtraArgs(expression) {
if (Array.isArray(expression)) {
if (expression.length === 0) {
return expression;
}
const op = expression[0];
if (op === 'literal') {
return expression;
}
if (
op === 'image' &&
expression.length === 3 &&
typeof expression[2] === 'object' &&
expression[2] !== null &&
!Array.isArray(expression[2])
) {
const newExpression = [
'image-config',
wrapImageExtraArgs(expression[1]),
['literal', expression[2]],
];
return newExpression;
}
const length = expression.length;
for (let i = 1; i < length; ++i) {
const arg = expression[i];
const newArg = wrapImageExtraArgs(arg);
if (newArg !== arg) {
const newExpression = [op];
for (let j = 1; j < i; ++j) {
newExpression.push(expression[j]);
}
newExpression.push(newArg);
for (let j = i + 1; j < length; ++j) {
newExpression.push(wrapImageExtraArgs(expression[j]));
}
return newExpression;
}
}
}
return expression;
}
// Shared config object for global expression evaluation context
export const styleConfig = {};
export const cameraObj = {zoom: 0, distanceFromCenter: 0};
// Add unsupported expressions to the MapLibre GL Style spec
CompoundExpression.register(expressions, {
...CompoundExpression.definitions,
'pitch': [{kind: 'number'}, [], (ctx) => cameraObj.pitch || 0],
'distance-from-center': [
{kind: 'number'},
[],
(ctx) => cameraObj.distanceFromCenter || 0,
],
'to-hsla': [
{kind: 'array', itemType: {kind: 'number'}, N: 4},
[{kind: 'string'}],
(ctx, [v]) => {
return rgbaToHsla(fromString(v.evaluate(ctx)));
},
],
'hsl': [
{kind: 'color'},
[{kind: 'number'}, {kind: 'number'}, {kind: 'number'}],
hsla,
],
'hsla': [
{kind: 'color'},
[{kind: 'number'}, {kind: 'number'}, {kind: 'number'}, {kind: 'number'}],
hsla,
],
'image-config': [
{kind: 'value'},
[{kind: 'string'}, {kind: 'value'}],
(ctx, [v, c]) => v.evaluate(ctx),
],
'measure-light': [{kind: 'number'}, [{kind: 'value'}], () => 1],
'config': [
{kind: 'value'},
[{kind: 'string'}],
(ctx, [key]) => {
const value = styleConfig[key.evaluate(ctx)];
return value === undefined ? {} : value;
},
],
});