Merge pull request #10214 from jahow/webgl-layer-boolean-operators
WebGL / Add 'case' operator for style expressions
This commit is contained in:
@@ -18,7 +18,7 @@ const period = 12; // animation period in seconds
|
||||
const animRatio =
|
||||
['^',
|
||||
['/',
|
||||
['mod',
|
||||
['%',
|
||||
['+',
|
||||
['time'],
|
||||
[
|
||||
|
||||
@@ -20,3 +20,7 @@ cloak:
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<div>Current sighting: <span id="info"></span></div>
|
||||
<div>
|
||||
Filter by UFO shape:
|
||||
<select id="shape-filter"></select>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,22 @@ import WebGLPointsLayer from '../src/ol/layer/WebGLPoints.js';
|
||||
|
||||
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
|
||||
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v4/mapbox.world-dark.json?secure&access_token=' + key,
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
})
|
||||
],
|
||||
target: document.getElementById('map'),
|
||||
view: new View({
|
||||
center: [0, 4000000],
|
||||
zoom: 2
|
||||
})
|
||||
});
|
||||
|
||||
const vectorSource = new Vector({
|
||||
features: [],
|
||||
attributions: 'National UFO Reporting Center'
|
||||
@@ -20,6 +36,15 @@ const newColor = [180, 255, 200];
|
||||
const size = 16;
|
||||
|
||||
const style = {
|
||||
variables: {
|
||||
filterShape: 'all'
|
||||
},
|
||||
filter: [
|
||||
'case',
|
||||
['!=', ['var', 'filterShape'], 'all'],
|
||||
['==', ['get', 'shape'], ['var', 'filterShape']],
|
||||
true
|
||||
],
|
||||
symbol: {
|
||||
symbolType: 'image',
|
||||
src: 'data/ufo_shapes.png',
|
||||
@@ -51,61 +76,71 @@ const style = {
|
||||
}
|
||||
};
|
||||
|
||||
function loadData() {
|
||||
const client = new XMLHttpRequest();
|
||||
client.open('GET', 'data/csv/ufo_sighting_data.csv');
|
||||
client.onload = function() {
|
||||
const csv = client.responseText;
|
||||
const features = [];
|
||||
|
||||
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
|
||||
|
||||
let curIndex;
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
|
||||
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
prevIndex = curIndex + 1;
|
||||
|
||||
const coords = fromLonLat([parseFloat(line[5]), parseFloat(line[4])]);
|
||||
|
||||
// only keep valid points
|
||||
if (isNaN(coords[0]) || isNaN(coords[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
features.push(new Feature({
|
||||
datetime: line[0],
|
||||
year: parseInt(/[0-9]{4}/.exec(line[0])[0]), // extract the year as int
|
||||
shape: line[2],
|
||||
duration: line[3],
|
||||
geometry: new Point(coords)
|
||||
}));
|
||||
}
|
||||
vectorSource.addFeatures(features);
|
||||
};
|
||||
client.send();
|
||||
// key is shape name, value is sightings count
|
||||
const shapeTypes = {
|
||||
all: 0
|
||||
};
|
||||
const shapeSelect = document.getElementById('shape-filter');
|
||||
shapeSelect.addEventListener('input', function() {
|
||||
style.variables.filterShape = shapeSelect.options[shapeSelect.selectedIndex].value;
|
||||
map.render();
|
||||
});
|
||||
function fillShapeSelect() {
|
||||
Object.keys(shapeTypes)
|
||||
.sort(function(a, b) {
|
||||
return shapeTypes[b] - shapeTypes[a];
|
||||
})
|
||||
.forEach(function(shape) {
|
||||
const option = document.createElement('option');
|
||||
option.text = `${shape} (${shapeTypes[shape]} sightings)`;
|
||||
option.value = shape;
|
||||
shapeSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
loadData();
|
||||
const client = new XMLHttpRequest();
|
||||
client.open('GET', 'data/csv/ufo_sighting_data.csv');
|
||||
client.onload = function() {
|
||||
const csv = client.responseText;
|
||||
const features = [];
|
||||
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new TileJSON({
|
||||
url: 'https://api.tiles.mapbox.com/v4/mapbox.world-dark.json?secure&access_token=' + key,
|
||||
crossOrigin: 'anonymous'
|
||||
})
|
||||
}),
|
||||
new WebGLPointsLayer({
|
||||
source: vectorSource,
|
||||
style: style
|
||||
})
|
||||
],
|
||||
target: document.getElementById('map'),
|
||||
view: new View({
|
||||
center: [0, 4000000],
|
||||
zoom: 2
|
||||
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
|
||||
|
||||
let curIndex;
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
|
||||
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
prevIndex = curIndex + 1;
|
||||
|
||||
const coords = fromLonLat([parseFloat(line[5]), parseFloat(line[4])]);
|
||||
|
||||
// only keep valid points
|
||||
if (isNaN(coords[0]) || isNaN(coords[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const shape = line[2];
|
||||
shapeTypes[shape] = (shapeTypes[shape] ? shapeTypes[shape] : 0) + 1;
|
||||
shapeTypes['all']++;
|
||||
|
||||
features.push(new Feature({
|
||||
datetime: line[0],
|
||||
year: parseInt(/[0-9]{4}/.exec(line[0])[0]), // extract the year as int
|
||||
shape: shape,
|
||||
duration: line[3],
|
||||
geometry: new Point(coords)
|
||||
}));
|
||||
}
|
||||
vectorSource.addFeatures(features);
|
||||
fillShapeSelect();
|
||||
};
|
||||
client.send();
|
||||
|
||||
map.addLayer(
|
||||
new WebGLPointsLayer({
|
||||
source: vectorSource,
|
||||
style: style
|
||||
})
|
||||
});
|
||||
);
|
||||
|
||||
const info = document.getElementById('info');
|
||||
map.on('pointermove', function(evt) {
|
||||
|
||||
@@ -23,10 +23,13 @@ import {asArray, isStringColor} from '../color.js';
|
||||
* * `['+', value1, value1]` adds `value1` and `value2`
|
||||
* * `['-', value1, value1]` subtracts `value2` from `value1`
|
||||
* * `['clamp', value, low, high]` clamps `value` between `low` and `high`
|
||||
* * `['mod', value1, value1]` returns the result of `value1 % value2` (modulo)
|
||||
* * `['%', value1, value1]` returns the result of `value1 % value2` (modulo)
|
||||
* * `['^', value1, value1]` returns the value of `value1` raised to the `value2` power
|
||||
*
|
||||
* * Transform operators:
|
||||
* * `['case', condition1, output1, ...conditionN, outputN, fallback]` selects the first output whose corresponding
|
||||
* condition evaluates to `true`. If no match is found, returns the `fallback` value.
|
||||
* All conditions should be `boolean`, output and fallback can be any kind.
|
||||
* * `['match', input, match1, output1, ...matchN, outputN, fallback]` compares the `input` value against all
|
||||
* provided `matchX` values, returning the output associated with the first valid match. If no match is found,
|
||||
* returns the `fallback` value.
|
||||
@@ -41,14 +44,15 @@ import {asArray, isStringColor} from '../color.js';
|
||||
* between `output1` and `outputN`.
|
||||
*
|
||||
* * Logical operators:
|
||||
* * `['<', value1, value2]` returns `1` if `value1` is strictly lower than value 2, or `0` otherwise.
|
||||
* * `['<=', value1, value2]` returns `1` if `value1` is lower than or equals value 2, or `0` otherwise.
|
||||
* * `['>', value1, value2]` returns `1` if `value1` is strictly greater than value 2, or `0` otherwise.
|
||||
* * `['>=', value1, value2]` returns `1` if `value1` is greater than or equals value 2, or `0` otherwise.
|
||||
* * `['==', value1, value2]` returns `1` if `value1` equals value 2, or `0` otherwise.
|
||||
* * `['!', value1]` returns `0` if `value1` strictly greater than `0`, or `1` otherwise.
|
||||
* * `['between', value1, value2, value3]` returns `1` if `value1` is contained between `value2` and `value3`
|
||||
* (inclusively), or `0` otherwise.
|
||||
* * `['<', value1, value2]` returns `true` if `value1` is strictly lower than value 2, or `false` otherwise.
|
||||
* * `['<=', value1, value2]` returns `true` if `value1` is lower than or equals value 2, or `false` otherwise.
|
||||
* * `['>', value1, value2]` returns `true` if `value1` is strictly greater than value 2, or `false` otherwise.
|
||||
* * `['>=', value1, value2]` returns `true` if `value1` is greater than or equals value 2, or `false` otherwise.
|
||||
* * `['==', value1, value2]` returns `true` if `value1` equals value 2, or `false` otherwise.
|
||||
* * `['!=', value1, value2]` returns `true` if `value1` equals value 2, or `false` otherwise.
|
||||
* * `['!', value1]` returns `false` if `value1` is `true` or greater than `0`, or `true` otherwise.
|
||||
* * `['between', value1, value2, value3]` returns `true` if `value1` is contained between `value2` and `value3`
|
||||
* (inclusively), or `false` otherwise.
|
||||
*
|
||||
* * Conversion operators:
|
||||
* * `['array', value1, ...valueN]` creates a numerical array from `number` values; please note that the amount of
|
||||
@@ -59,6 +63,7 @@ import {asArray, isStringColor} from '../color.js';
|
||||
*
|
||||
* Values can either be literals or another operator, as they will be evaluated recursively.
|
||||
* Literal values can be of the following types:
|
||||
* * `boolean`
|
||||
* * `number`
|
||||
* * `string`
|
||||
* * {@link module:ol/color~Color}
|
||||
@@ -252,9 +257,9 @@ function assertNumber(value) {
|
||||
throw new Error(`A numeric value was expected, got ${JSON.stringify(value)} instead`);
|
||||
}
|
||||
}
|
||||
function assertNumbers(arr) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
assertNumber(arr[i]);
|
||||
function assertNumbers(values) {
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
assertNumber(values[i]);
|
||||
}
|
||||
}
|
||||
function assertString(value) {
|
||||
@@ -287,6 +292,11 @@ function assertArgsEven(args) {
|
||||
throw new Error(`An even amount of arguments was expected, got ${args} instead`);
|
||||
}
|
||||
}
|
||||
function assertArgsOdd(args) {
|
||||
if (args.length % 2 === 0) {
|
||||
throw new Error(`An even amount of arguments was expected, got ${args} instead`);
|
||||
}
|
||||
}
|
||||
function assertUniqueInferredType(args, types) {
|
||||
if (!isTypeUnique(types)) {
|
||||
throw new Error(`Could not infer only one type from the following expression: ${JSON.stringify(args)}`);
|
||||
@@ -349,6 +359,7 @@ Operators['resolution'] = {
|
||||
return 'u_resolution';
|
||||
}
|
||||
};
|
||||
|
||||
Operators['*'] = {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.NUMBER;
|
||||
@@ -401,7 +412,7 @@ Operators['clamp'] = {
|
||||
return `clamp(${expressionToGlsl(context, args[0])}, ${min}, ${max})`;
|
||||
}
|
||||
};
|
||||
Operators['mod'] = {
|
||||
Operators['%'] = {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.NUMBER;
|
||||
},
|
||||
@@ -421,6 +432,7 @@ Operators['^'] = {
|
||||
return `pow(${expressionToGlsl(context, args[0])}, ${expressionToGlsl(context, args[1])})`;
|
||||
}
|
||||
};
|
||||
|
||||
Operators['>'] = {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.BOOLEAN;
|
||||
@@ -461,16 +473,31 @@ Operators['<='] = {
|
||||
return `(${expressionToGlsl(context, args[0])} <= ${expressionToGlsl(context, args[1])})`;
|
||||
}
|
||||
};
|
||||
Operators['=='] = {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.BOOLEAN;
|
||||
},
|
||||
toGlsl: function(context, args) {
|
||||
assertArgsCount(args, 2);
|
||||
assertNumbers(args);
|
||||
return `(${expressionToGlsl(context, args[0])} == ${expressionToGlsl(context, args[1])})`;
|
||||
}
|
||||
};
|
||||
|
||||
function getEqualOperator(operator) {
|
||||
return {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.BOOLEAN;
|
||||
},
|
||||
toGlsl: function(context, args) {
|
||||
assertArgsCount(args, 2);
|
||||
|
||||
// find common type
|
||||
let type = ValueTypes.ANY;
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
type = type & getValueType(args[i]);
|
||||
}
|
||||
if (type === 0) {
|
||||
throw new Error(`All arguments should be of compatible type, got ${JSON.stringify(args)} instead`);
|
||||
}
|
||||
|
||||
return `(${expressionToGlsl(context, args[0], type)} ${operator} ${expressionToGlsl(context, args[1], type)})`;
|
||||
}
|
||||
};
|
||||
}
|
||||
Operators['=='] = getEqualOperator('==');
|
||||
Operators['!='] = getEqualOperator('!=');
|
||||
|
||||
Operators['!'] = {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.BOOLEAN;
|
||||
@@ -494,6 +521,7 @@ Operators['between'] = {
|
||||
return `(${value} >= ${min} && ${value} <= ${max})`;
|
||||
}
|
||||
};
|
||||
|
||||
Operators['array'] = {
|
||||
getReturnType: function(args) {
|
||||
return ValueTypes.NUMBER_ARRAY;
|
||||
@@ -526,6 +554,7 @@ Operators['color'] = {
|
||||
return `vec${args.length}(${parsedArgs.join(', ')})`;
|
||||
}
|
||||
};
|
||||
|
||||
Operators['interpolate'] = {
|
||||
getReturnType: function(args) {
|
||||
let type = ValueTypes.COLOR | ValueTypes.NUMBER;
|
||||
@@ -580,7 +609,6 @@ Operators['match'] = {
|
||||
assertArgsEven(args);
|
||||
assertArgsMinCount(args, 4);
|
||||
|
||||
// compute input/output types
|
||||
const typeHint = opt_typeHint !== undefined ? opt_typeHint : ValueTypes.ANY;
|
||||
const outputType = Operators['match'].getReturnType(args) & typeHint;
|
||||
assertUniqueInferredType(args, outputType);
|
||||
@@ -596,3 +624,33 @@ Operators['match'] = {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
Operators['case'] = {
|
||||
getReturnType: function(args) {
|
||||
let type = ValueTypes.ANY;
|
||||
for (let i = 1; i < args.length; i += 2) {
|
||||
type = type & getValueType(args[i]);
|
||||
}
|
||||
type = type & getValueType(args[args.length - 1]);
|
||||
return type;
|
||||
},
|
||||
toGlsl: function(context, args, opt_typeHint) {
|
||||
assertArgsOdd(args);
|
||||
assertArgsMinCount(args, 3);
|
||||
|
||||
const typeHint = opt_typeHint !== undefined ? opt_typeHint : ValueTypes.ANY;
|
||||
const outputType = Operators['case'].getReturnType(args) & typeHint;
|
||||
assertUniqueInferredType(args, outputType);
|
||||
for (let i = 0; i < args.length - 1; i += 2) {
|
||||
assertBoolean(args[i]);
|
||||
}
|
||||
|
||||
const fallback = expressionToGlsl(context, args[args.length - 1], outputType);
|
||||
let result = null;
|
||||
for (let i = args.length - 3; i >= 0; i -= 2) {
|
||||
const condition = expressionToGlsl(context, args[i]);
|
||||
const output = expressionToGlsl(context, args[i + 1], outputType);
|
||||
result = `(${condition} ? ${output} : ${result || fallback})`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @module ol/webgl/ShaderBuilder
|
||||
*/
|
||||
|
||||
import {expressionToGlsl, ValueTypes} from '../style/expressions.js';
|
||||
import {expressionToGlsl, stringToGlsl, ValueTypes} from '../style/expressions.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} VaryingDescription
|
||||
@@ -445,8 +445,14 @@ export function parseLiteralStyle(style) {
|
||||
fragContext.variables.forEach(function(varName) {
|
||||
builder.addUniform(`float u_${varName}`);
|
||||
uniforms[`u_${varName}`] = function() {
|
||||
return style.variables && style.variables[varName] !== undefined ?
|
||||
style.variables[varName] : 0;
|
||||
if (!style.variables || style.variables[varName] === undefined) {
|
||||
throw new Error(`The following variable is missing from the style: ${varName}`);
|
||||
}
|
||||
let value = style.variables[varName];
|
||||
if (typeof value === 'string') {
|
||||
value = parseFloat(stringToGlsl(vertContext, value));
|
||||
}
|
||||
return value !== undefined ? value : -9999999; // to avoid matching with the first string literal
|
||||
};
|
||||
});
|
||||
|
||||
@@ -481,7 +487,7 @@ export function parseLiteralStyle(style) {
|
||||
callback: function(feature) {
|
||||
let value = feature.get(attributeName);
|
||||
if (typeof value === 'string') {
|
||||
value = vertContext.stringLiteralsMap[value];
|
||||
value = parseFloat(stringToGlsl(vertContext, value));
|
||||
}
|
||||
return value !== undefined ? value : -9999999; // to avoid matching with the first string literal
|
||||
}
|
||||
|
||||
@@ -130,12 +130,13 @@ describe('ol.style.expressions', function() {
|
||||
expect(getValueType(['*', ['get', 'size'], 12])).to.eql(ValueTypes.NUMBER);
|
||||
expect(getValueType(['clamp', ['get', 'attr2'], ['get', 'attr3'], 20])).to.eql(ValueTypes.NUMBER);
|
||||
expect(getValueType(['^', 10, 2])).to.eql(ValueTypes.NUMBER);
|
||||
expect(getValueType(['mod', ['time'], 10])).to.eql(ValueTypes.NUMBER);
|
||||
expect(getValueType(['%', ['time'], 10])).to.eql(ValueTypes.NUMBER);
|
||||
expect(getValueType(['>', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['>=', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['<', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['<=', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['==', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['!=', 10, ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['between', ['get', 'attr4'], -4.0, 5.0])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['!', ['get', 'attr4']])).to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['array', ['get', 'attr4'], 1, 2, 3])).to.eql(ValueTypes.NUMBER_ARRAY);
|
||||
@@ -164,12 +165,13 @@ describe('ol.style.expressions', function() {
|
||||
expect(expressionToGlsl(context, ['+', ['*', ['get', 'size'], 0.001], 12])).to.eql('((a_size * 0.001) + 12.0)');
|
||||
expect(expressionToGlsl(context, ['/', ['-', ['get', 'size'], 20], 100])).to.eql('((a_size - 20.0) / 100.0)');
|
||||
expect(expressionToGlsl(context, ['clamp', ['get', 'attr2'], ['get', 'attr3'], 20])).to.eql('clamp(a_attr2, a_attr3, 20.0)');
|
||||
expect(expressionToGlsl(context, ['^', ['mod', ['time'], 10], 2])).to.eql('pow(mod(u_time, 10.0), 2.0)');
|
||||
expect(expressionToGlsl(context, ['^', ['%', ['time'], 10], 2])).to.eql('pow(mod(u_time, 10.0), 2.0)');
|
||||
expect(expressionToGlsl(context, ['>', 10, ['get', 'attr4']])).to.eql('(10.0 > a_attr4)');
|
||||
expect(expressionToGlsl(context, ['>=', 10, ['get', 'attr4']])).to.eql('(10.0 >= a_attr4)');
|
||||
expect(expressionToGlsl(context, ['<', 10, ['get', 'attr4']])).to.eql('(10.0 < a_attr4)');
|
||||
expect(expressionToGlsl(context, ['<=', 10, ['get', 'attr4']])).to.eql('(10.0 <= a_attr4)');
|
||||
expect(expressionToGlsl(context, ['==', 10, ['get', 'attr4']])).to.eql('(10.0 == a_attr4)');
|
||||
expect(expressionToGlsl(context, ['!=', 10, ['get', 'attr4']])).to.eql('(10.0 != a_attr4)');
|
||||
expect(expressionToGlsl(context, ['between', ['get', 'attr4'], -4.0, 5.0])).to.eql('(a_attr4 >= -4.0 && a_attr4 <= 5.0)');
|
||||
expect(expressionToGlsl(context, ['!', ['get', 'attr4']])).to.eql('(!a_attr4)');
|
||||
expect(expressionToGlsl(context, ['array', ['get', 'attr4'], 1, 2, 3])).to.eql('vec4(a_attr4, 1.0, 2.0, 3.0)');
|
||||
@@ -292,6 +294,94 @@ describe('ol.style.expressions', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('case operator', function() {
|
||||
let context;
|
||||
|
||||
beforeEach(function() {
|
||||
context = {
|
||||
variables: [],
|
||||
attributes: [],
|
||||
stringLiteralsMap: {}
|
||||
};
|
||||
});
|
||||
|
||||
it('correctly guesses the output type', function() {
|
||||
expect(getValueType(['case', true, 0, false, [3, 4, 5], 'green']))
|
||||
.to.eql(ValueTypes.NONE);
|
||||
expect(getValueType(['case', true, 0, false, 1, 2]))
|
||||
.to.eql(ValueTypes.NUMBER);
|
||||
expect(getValueType(['case', true, [0, 0, 0], true, [1, 2, 3], ['get', 'attr'], [4, 5, 6, 7], [8, 9, 0]]))
|
||||
.to.eql(ValueTypes.COLOR | ValueTypes.NUMBER_ARRAY);
|
||||
expect(getValueType(['case', true, 'red', true, 'yellow', ['get', 'attr'], 'green', 'white']))
|
||||
.to.eql(ValueTypes.COLOR | ValueTypes.STRING);
|
||||
expect(getValueType(['case', true, [0, 0], false, [1, 1], [2, 2]]))
|
||||
.to.eql(ValueTypes.NUMBER_ARRAY);
|
||||
});
|
||||
|
||||
it('throws if no single output type could be inferred', function() {
|
||||
let thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['case', false, 'red', true, 'yellow', 'green'], ValueTypes.COLOR);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(false);
|
||||
|
||||
try {
|
||||
expressionToGlsl(context, ['case', true, 'red', true, 'yellow', 'green']);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
|
||||
thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['case', true, 'red', false, 'yellow', 'green'], ValueTypes.NUMBER);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
|
||||
thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['case', true, 'red', false, 'yellow', 'not_a_color'], ValueTypes.COLOR);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
});
|
||||
|
||||
it('throws if invalid argument count', function() {
|
||||
let thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['case', true, 0, false, 1]);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
|
||||
thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['case', true, 0]);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
|
||||
try {
|
||||
expressionToGlsl(context, ['case', false]);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
});
|
||||
|
||||
it('correctly parses the expression (colors)', function() {
|
||||
expect(expressionToGlsl(context, ['case', ['>', ['get', 'attr'], 3], 'red', ['>', ['get', 'attr'], 1], 'yellow', 'white'], ValueTypes.COLOR))
|
||||
.to.eql('((a_attr > 3.0) ? vec4(1.0, 0.0, 0.0, 1.0) : ((a_attr > 1.0) ? vec4(1.0, 1.0, 0.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0)))');
|
||||
});
|
||||
});
|
||||
|
||||
describe('match operator', function() {
|
||||
let context;
|
||||
|
||||
@@ -529,7 +619,7 @@ describe('ol.style.expressions', function() {
|
||||
['linear'],
|
||||
['^',
|
||||
['/',
|
||||
['mod',
|
||||
['%',
|
||||
['+',
|
||||
['time'],
|
||||
[
|
||||
|
||||
@@ -402,6 +402,56 @@ void main(void) {
|
||||
expect(result.attributes).to.eql([]);
|
||||
expect(result.uniforms).to.have.property('u_ratio');
|
||||
});
|
||||
|
||||
it('correctly adds string variables to the string literals mapping', function() {
|
||||
const result = parseLiteralStyle({
|
||||
variables: {
|
||||
mySize: 'abcdef'
|
||||
},
|
||||
symbol: {
|
||||
symbolType: 'square',
|
||||
size: ['match', ['var', 'mySize'], 'abc', 10, 'def', 20, 30],
|
||||
color: 'red'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.uniforms['u_mySize']()).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('throws when a variable is requested but not present in the style', function(done) {
|
||||
const result = parseLiteralStyle({
|
||||
variables: {},
|
||||
symbol: {
|
||||
symbolType: 'square',
|
||||
size: ['var', 'mySize'],
|
||||
color: 'red'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
result.uniforms['u_mySize']();
|
||||
} catch (e) {
|
||||
done();
|
||||
}
|
||||
done(true);
|
||||
});
|
||||
|
||||
it('throws when a variable is requested but the style does not have a variables dict', function(done) {
|
||||
const result = parseLiteralStyle({
|
||||
symbol: {
|
||||
symbolType: 'square',
|
||||
size: ['var', 'mySize'],
|
||||
color: 'red'
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
result.uniforms['u_mySize']();
|
||||
} catch (e) {
|
||||
done();
|
||||
}
|
||||
done(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user