Support expressions for band arguments
This commit is contained in:
6
examples/cog-stretch.css
Normal file
6
examples/cog-stretch.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.controls {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto 1fr;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0 1em;
|
||||||
|
}
|
||||||
44
examples/cog-stretch.html
Normal file
44
examples/cog-stretch.html
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Band Constrast Stretch
|
||||||
|
shortdesc: Choosing bands and applying constrast stretch
|
||||||
|
docs: >
|
||||||
|
This example uses the `layer.updateStyleVariables()` method to update the rendering
|
||||||
|
of a GeoTIFF based on user selected bands and contrast stretch parameters.
|
||||||
|
tags: "cog, webgl, style"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
<div class="controls">
|
||||||
|
<label for="red">Red channel</label>
|
||||||
|
<select id="red">
|
||||||
|
<option value="1" selected>visible red</option>
|
||||||
|
<option value="2">visible green</option>
|
||||||
|
<option value="3">visible blue</option>
|
||||||
|
<option value="4">near infrared</option>
|
||||||
|
</select>
|
||||||
|
<label>max
|
||||||
|
<input type="range" id="redMax" value="3000" min="2000" max="5000">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="green">Green channel</label>
|
||||||
|
<select id="green">
|
||||||
|
<option value="1">visible red</option>
|
||||||
|
<option value="2" selected>visible green</option>
|
||||||
|
<option value="3">visible blue</option>
|
||||||
|
<option value="4">near infrared</option>
|
||||||
|
</select>
|
||||||
|
<label>max
|
||||||
|
<input type="range" id="greenMax" value="3000" min="2000" max="5000">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label for="blue">Blue channel</label>
|
||||||
|
<select id="blue">
|
||||||
|
<option value="1">visible red</option>
|
||||||
|
<option value="2">visible green</option>
|
||||||
|
<option value="3" selected>visible blue</option>
|
||||||
|
<option value="4">near infrared</option>
|
||||||
|
</select>
|
||||||
|
<label>max
|
||||||
|
<input type="range" id="blueMax" value="3000" min="2000" max="5000">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
62
examples/cog-stretch.js
Normal file
62
examples/cog-stretch.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import GeoTIFF from '../src/ol/source/GeoTIFF.js';
|
||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import TileLayer from '../src/ol/layer/WebGLTile.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
|
||||||
|
const channels = ['red', 'green', 'blue'];
|
||||||
|
for (const channel of channels) {
|
||||||
|
const selector = document.getElementById(channel);
|
||||||
|
selector.addEventListener('change', update);
|
||||||
|
|
||||||
|
const input = document.getElementById(`${channel}Max`);
|
||||||
|
input.addEventListener('input', update);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVariables() {
|
||||||
|
const variables = {};
|
||||||
|
for (const channel of channels) {
|
||||||
|
const selector = document.getElementById(channel);
|
||||||
|
variables[channel] = parseInt(selector.value, 10);
|
||||||
|
|
||||||
|
const inputId = `${channel}Max`;
|
||||||
|
const input = document.getElementById(inputId);
|
||||||
|
variables[inputId] = parseInt(input.value, 10);
|
||||||
|
}
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
const layer = new TileLayer({
|
||||||
|
style: {
|
||||||
|
variables: getVariables(),
|
||||||
|
color: [
|
||||||
|
'array',
|
||||||
|
['/', ['band', ['var', 'red']], ['var', 'redMax']],
|
||||||
|
['/', ['band', ['var', 'green']], ['var', 'greenMax']],
|
||||||
|
['/', ['band', ['var', 'blue']], ['var', 'blueMax']],
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: new GeoTIFF({
|
||||||
|
normalize: false,
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
url: 'https://s2downloads.eox.at/demo/EOxCloudless/2020/rgbnir/s2cloudless2020-16bits_sinlge-file_z0-4.tif',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
layer.updateStyleVariables(getVariables());
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
target: 'map',
|
||||||
|
layers: [layer],
|
||||||
|
view: new View({
|
||||||
|
projection: 'EPSG:4326',
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2,
|
||||||
|
maxZoom: 6,
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -105,6 +105,7 @@ function parseStyle(style, bandCount) {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
bandCount: bandCount,
|
bandCount: bandCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -203,14 +204,15 @@ function parseStyle(style, bandCount) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const textureCount = Math.ceil(bandCount / 4);
|
const textureCount = Math.ceil(bandCount / 4);
|
||||||
const colorAssignments = new Array(textureCount);
|
uniformDeclarations.push(
|
||||||
for (let textureIndex = 0; textureIndex < textureCount; ++textureIndex) {
|
`uniform sampler2D ${Uniforms.TILE_TEXTURE_ARRAY}[${textureCount}];`
|
||||||
const uniformName = Uniforms.TILE_TEXTURE_PREFIX + textureIndex;
|
);
|
||||||
uniformDeclarations.push(`uniform sampler2D ${uniformName};`);
|
|
||||||
colorAssignments[
|
const functionDefintions = Object.keys(context.functions).map(function (
|
||||||
textureIndex
|
name
|
||||||
] = `vec4 color${textureIndex} = texture2D(${uniformName}, v_textureCoord);`;
|
) {
|
||||||
}
|
return context.functions[name];
|
||||||
|
});
|
||||||
|
|
||||||
const fragmentShader = `
|
const fragmentShader = `
|
||||||
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
@@ -228,10 +230,12 @@ function parseStyle(style, bandCount) {
|
|||||||
|
|
||||||
${uniformDeclarations.join('\n')}
|
${uniformDeclarations.join('\n')}
|
||||||
|
|
||||||
void main() {
|
${functionDefintions.join('\n')}
|
||||||
${colorAssignments.join('\n')}
|
|
||||||
|
|
||||||
vec4 color = color0;
|
void main() {
|
||||||
|
vec4 color = texture2D(${
|
||||||
|
Uniforms.TILE_TEXTURE_ARRAY
|
||||||
|
}[0], v_textureCoord);
|
||||||
|
|
||||||
${pipeline.join('\n')}
|
${pipeline.join('\n')}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {numberSafeCompareFunction} from '../../array.js';
|
|||||||
import {toSize} from '../../size.js';
|
import {toSize} from '../../size.js';
|
||||||
|
|
||||||
export const Uniforms = {
|
export const Uniforms = {
|
||||||
TILE_TEXTURE_PREFIX: 'u_tileTexture',
|
TILE_TEXTURE_ARRAY: 'u_tileTextures',
|
||||||
TILE_TRANSFORM: 'u_tileTransform',
|
TILE_TRANSFORM: 'u_tileTransform',
|
||||||
TRANSITION_ALPHA: 'u_transitionAlpha',
|
TRANSITION_ALPHA: 'u_transitionAlpha',
|
||||||
DEPTH: 'u_depth',
|
DEPTH: 'u_depth',
|
||||||
@@ -516,7 +516,7 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
|||||||
++textureIndex
|
++textureIndex
|
||||||
) {
|
) {
|
||||||
const textureProperty = 'TEXTURE' + textureIndex;
|
const textureProperty = 'TEXTURE' + textureIndex;
|
||||||
const uniformName = Uniforms.TILE_TEXTURE_PREFIX + textureIndex;
|
const uniformName = `${Uniforms.TILE_TEXTURE_ARRAY}[${textureIndex}]`;
|
||||||
gl.activeTexture(gl[textureProperty]);
|
gl.activeTexture(gl[textureProperty]);
|
||||||
gl.bindTexture(gl.TEXTURE_2D, tileTexture.textures[textureIndex]);
|
gl.bindTexture(gl.TEXTURE_2D, tileTexture.textures[textureIndex]);
|
||||||
gl.uniform1i(
|
gl.uniform1i(
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ export function isTypeUnique(valueType) {
|
|||||||
* @property {Array<string>} variables List of variables used in the expression; contains **unprefixed names**
|
* @property {Array<string>} variables List of variables used in the expression; contains **unprefixed names**
|
||||||
* @property {Array<string>} attributes List of attributes used in the expression; contains **unprefixed names**
|
* @property {Array<string>} attributes List of attributes used in the expression; contains **unprefixed names**
|
||||||
* @property {Object<string, number>} stringLiteralsMap This object maps all encountered string values to a number
|
* @property {Object<string, number>} stringLiteralsMap This object maps all encountered string values to a number
|
||||||
|
* @property {Object<string, string>} functions Lookup of functions used by the style.
|
||||||
* @property {number} [bandCount] Number of bands per pixel.
|
* @property {number} [bandCount] Number of bands per pixel.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -417,6 +418,8 @@ Operators['var'] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const GET_BAND_VALUE_FUNC = 'getBandValue';
|
||||||
|
|
||||||
Operators['band'] = {
|
Operators['band'] = {
|
||||||
getReturnType: function (args) {
|
getReturnType: function (args) {
|
||||||
return ValueTypes.NUMBER;
|
return ValueTypes.NUMBER;
|
||||||
@@ -425,32 +428,38 @@ Operators['band'] = {
|
|||||||
assertArgsMinCount(args, 1);
|
assertArgsMinCount(args, 1);
|
||||||
assertArgsMaxCount(args, 3);
|
assertArgsMaxCount(args, 3);
|
||||||
const band = args[0];
|
const band = args[0];
|
||||||
if (typeof band !== 'number') {
|
|
||||||
throw new Error('Band index must be a number');
|
if (!(GET_BAND_VALUE_FUNC in context.functions)) {
|
||||||
}
|
let ifBlocks = '';
|
||||||
const zeroBasedBand = band - 1;
|
const bandCount = context.bandCount || 1;
|
||||||
const colorIndex = Math.floor(zeroBasedBand / 4);
|
for (let i = 0; i < bandCount; i++) {
|
||||||
let bandIndex = zeroBasedBand % 4;
|
const colorIndex = Math.floor(i / 4);
|
||||||
if (band === context.bandCount && bandIndex === 1) {
|
let bandIndex = i % 4;
|
||||||
// LUMINANCE_ALPHA - band 1 assigned to rgb and band 2 assigned to alpha
|
if (bandIndex === bandCount - 1 && bandIndex === 1) {
|
||||||
bandIndex = 3;
|
// LUMINANCE_ALPHA - band 1 assigned to rgb and band 2 assigned to alpha
|
||||||
}
|
bandIndex = 3;
|
||||||
if (args.length === 1) {
|
}
|
||||||
return `color${colorIndex}[${bandIndex}]`;
|
const textureName = `${Uniforms.TILE_TEXTURE_ARRAY}[${colorIndex}]`;
|
||||||
} else {
|
ifBlocks += `
|
||||||
const xOffset = args[1];
|
if (band == ${i + 1}.0) {
|
||||||
const yOffset = args[2] || 0;
|
return texture2D(${textureName}, v_textureCoord + vec2(dx, dy))[${bandIndex}];
|
||||||
assertNumber(xOffset);
|
}
|
||||||
assertNumber(yOffset);
|
`;
|
||||||
const uniformName = Uniforms.TILE_TEXTURE_PREFIX + colorIndex;
|
}
|
||||||
return `texture2D(${uniformName}, v_textureCoord + vec2(${expressionToGlsl(
|
|
||||||
context,
|
context.functions[GET_BAND_VALUE_FUNC] = `
|
||||||
xOffset
|
float getBandValue(float band, float xOffset, float yOffset) {
|
||||||
)} / ${Uniforms.TEXTURE_PIXEL_WIDTH}, ${expressionToGlsl(
|
float dx = xOffset / ${Uniforms.TEXTURE_PIXEL_WIDTH};
|
||||||
context,
|
float dy = yOffset / ${Uniforms.TEXTURE_PIXEL_HEIGHT};
|
||||||
yOffset
|
${ifBlocks}
|
||||||
)} / ${Uniforms.TEXTURE_PIXEL_HEIGHT}))[${bandIndex}]`;
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bandExpression = expressionToGlsl(context, band);
|
||||||
|
const xOffsetExpression = expressionToGlsl(context, args[1] || 0);
|
||||||
|
const yOffsetExpression = expressionToGlsl(context, args[2] || 0);
|
||||||
|
return `${GET_BAND_VALUE_FUNC}(${bandExpression}, ${xOffsetExpression}, ${yOffsetExpression})`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -441,6 +441,7 @@ export function parseLiteralStyle(style) {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
const parsedSize = expressionToGlsl(
|
const parsedSize = expressionToGlsl(
|
||||||
vertContext,
|
vertContext,
|
||||||
@@ -471,6 +472,7 @@ export function parseLiteralStyle(style) {
|
|||||||
variables: vertContext.variables,
|
variables: vertContext.variables,
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: vertContext.stringLiteralsMap,
|
stringLiteralsMap: vertContext.stringLiteralsMap,
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
const parsedColor = expressionToGlsl(fragContext, color, ValueTypes.COLOR);
|
const parsedColor = expressionToGlsl(fragContext, color, ValueTypes.COLOR);
|
||||||
const parsedOpacity = expressionToGlsl(
|
const parsedOpacity = expressionToGlsl(
|
||||||
|
|||||||
@@ -96,10 +96,9 @@ describe('ol/layer/WebGLTile', function () {
|
|||||||
uniform float u_var_r;
|
uniform float u_var_r;
|
||||||
uniform float u_var_g;
|
uniform float u_var_g;
|
||||||
uniform float u_var_b;
|
uniform float u_var_b;
|
||||||
uniform sampler2D u_tileTexture0;
|
uniform sampler2D u_tileTextures[1];
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color0 = texture2D(u_tileTexture0, v_textureCoord);
|
vec4 color = texture2D(u_tileTextures[0], v_textureCoord);
|
||||||
vec4 color = color0;
|
|
||||||
color = vec4(u_var_r / 255.0, u_var_g / 255.0, u_var_b / 255.0, 1.0);
|
color = vec4(u_var_r / 255.0, u_var_g / 255.0, u_var_b / 255.0, 1.0);
|
||||||
if (color.a == 0.0) {
|
if (color.a == 0.0) {
|
||||||
discard;
|
discard;
|
||||||
@@ -125,6 +124,82 @@ describe('ol/layer/WebGLTile', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adds a getBandValue function to the fragment shaders', function () {
|
||||||
|
const max = 3000;
|
||||||
|
function normalize(value) {
|
||||||
|
return ['/', value, max];
|
||||||
|
}
|
||||||
|
|
||||||
|
const red = normalize(['band', 1]);
|
||||||
|
const green = normalize(['band', 2]);
|
||||||
|
const nir = normalize(['band', 4]);
|
||||||
|
|
||||||
|
layer.setStyle({
|
||||||
|
color: ['array', nir, red, green, 1],
|
||||||
|
});
|
||||||
|
|
||||||
|
const compileShaderSpy = sinon.spy(WebGLHelper.prototype, 'compileShader');
|
||||||
|
const renderer = layer.createRenderer();
|
||||||
|
const viewState = map.getView().getState();
|
||||||
|
const size = map.getSize();
|
||||||
|
const frameState = {
|
||||||
|
viewState: viewState,
|
||||||
|
extent: getForViewAndSize(
|
||||||
|
viewState.center,
|
||||||
|
viewState.resolution,
|
||||||
|
viewState.rotation,
|
||||||
|
size
|
||||||
|
),
|
||||||
|
layerStatesArray: map.getLayerGroup().getLayerStatesArray(),
|
||||||
|
layerIndex: 0,
|
||||||
|
};
|
||||||
|
renderer.prepareFrame(frameState);
|
||||||
|
compileShaderSpy.restore();
|
||||||
|
expect(compileShaderSpy.callCount).to.be(2);
|
||||||
|
expect(compileShaderSpy.getCall(0).args[0].replace(/[ \n]+/g, ' ')).to.be(
|
||||||
|
`
|
||||||
|
#ifdef GL_FRAGMENT_PRECISION_HIGH
|
||||||
|
precision highp float;
|
||||||
|
#else
|
||||||
|
precision mediump float;
|
||||||
|
#endif varying vec2 v_textureCoord;
|
||||||
|
uniform float u_transitionAlpha;
|
||||||
|
uniform float u_texturePixelWidth;
|
||||||
|
uniform float u_texturePixelHeight;
|
||||||
|
uniform float u_resolution;
|
||||||
|
uniform float u_zoom;
|
||||||
|
uniform sampler2D u_tileTextures[1];
|
||||||
|
|
||||||
|
float getBandValue(float band, float xOffset, float yOffset) {
|
||||||
|
float dx = xOffset / u_texturePixelWidth;
|
||||||
|
float dy = yOffset / u_texturePixelHeight;
|
||||||
|
if (band == 1.0) {
|
||||||
|
return texture2D(u_tileTextures[0], v_textureCoord + vec2(dx, dy))[0];
|
||||||
|
}
|
||||||
|
if (band == 2.0) {
|
||||||
|
return texture2D(u_tileTextures[0], v_textureCoord + vec2(dx, dy))[1];
|
||||||
|
}
|
||||||
|
if (band == 3.0) {
|
||||||
|
return texture2D(u_tileTextures[0], v_textureCoord + vec2(dx, dy))[2];
|
||||||
|
}
|
||||||
|
if (band == 4.0) {
|
||||||
|
return texture2D(u_tileTextures[0], v_textureCoord + vec2(dx, dy))[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 color = texture2D(u_tileTextures[0], v_textureCoord);
|
||||||
|
color = vec4((getBandValue(4.0, 0.0, 0.0) / 3000.0), (getBandValue(1.0, 0.0, 0.0) / 3000.0), (getBandValue(2.0, 0.0, 0.0) / 3000.0), 1.0);
|
||||||
|
if (color.a == 0.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
gl_FragColor = color;
|
||||||
|
gl_FragColor.rgb *= gl_FragColor.a;
|
||||||
|
gl_FragColor *= u_transitionAlpha;
|
||||||
|
}`.replace(/[ \n]+/g, ' ')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
describe('updateStyleVariables()', function () {
|
describe('updateStyleVariables()', function () {
|
||||||
it('updates style variables', function (done) {
|
it('updates style variables', function (done) {
|
||||||
layer.updateStyleVariables({
|
layer.updateStyleVariables({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
uniformNameForVariable,
|
uniformNameForVariable,
|
||||||
} from '../../../../../src/ol/style/expressions.js';
|
} from '../../../../../src/ol/style/expressions.js';
|
||||||
|
|
||||||
describe('ol.style.expressions', function () {
|
describe('ol/style/expressions', function () {
|
||||||
describe('numberToGlsl', function () {
|
describe('numberToGlsl', function () {
|
||||||
it('does a simple transform when a fraction is present', function () {
|
it('does a simple transform when a fraction is present', function () {
|
||||||
expect(numberToGlsl(1.3456)).to.eql('1.3456');
|
expect(numberToGlsl(1.3456)).to.eql('1.3456');
|
||||||
@@ -70,6 +70,7 @@ describe('ol.style.expressions', function () {
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
context = {
|
context = {
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,6 +208,7 @@ describe('ol.style.expressions', function () {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -289,9 +291,11 @@ describe('ol.style.expressions', function () {
|
|||||||
expect(
|
expect(
|
||||||
expressionToGlsl(context, ['color', ['get', 'attr4'], 1, 2, 0.5])
|
expressionToGlsl(context, ['color', ['get', 'attr4'], 1, 2, 0.5])
|
||||||
).to.eql('vec4(a_attr4 / 255.0, 1.0 / 255.0, 2.0 / 255.0, 0.5)');
|
).to.eql('vec4(a_attr4 / 255.0, 1.0 / 255.0, 2.0 / 255.0, 0.5)');
|
||||||
expect(expressionToGlsl(context, ['band', 1])).to.eql('color0[0]');
|
expect(expressionToGlsl(context, ['band', 1])).to.eql(
|
||||||
|
'getBandValue(1.0, 0.0, 0.0)'
|
||||||
|
);
|
||||||
expect(expressionToGlsl(context, ['band', 1, -1, 2])).to.eql(
|
expect(expressionToGlsl(context, ['band', 1, -1, 2])).to.eql(
|
||||||
'texture2D(u_tileTexture0, v_textureCoord + vec2(-1.0 / u_texturePixelWidth, 2.0 / u_texturePixelHeight))[0]'
|
'getBandValue(1.0, -1.0, 2.0)'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -464,6 +468,7 @@ describe('ol.style.expressions', function () {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -608,6 +613,7 @@ describe('ol.style.expressions', function () {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -821,6 +827,7 @@ describe('ol.style.expressions', function () {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1067,6 +1074,7 @@ describe('ol.style.expressions', function () {
|
|||||||
variables: [],
|
variables: [],
|
||||||
attributes: [],
|
attributes: [],
|
||||||
stringLiteralsMap: {},
|
stringLiteralsMap: {},
|
||||||
|
functions: {},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user