Expressions / introduce the match operator
This operator allows mapping from input values to output values of different kinds and handles variable arguments count.
This commit is contained in:
@@ -503,5 +503,33 @@ export const Operators = {
|
||||
const end = expressionToGlsl(newContext, args[2], ValueTypes.COLOR);
|
||||
return `mix(${start}, ${end}, ${expressionToGlsl(context, args[0])})`;
|
||||
}
|
||||
},
|
||||
'match': {
|
||||
getReturnType: function(args) {
|
||||
let type = ValueTypes.ANY;
|
||||
for (let i = 2; 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) {
|
||||
assertArgsEven(args);
|
||||
|
||||
// compute input/output types
|
||||
const typeHint = opt_typeHint !== undefined ? opt_typeHint : ValueTypes.ANY;
|
||||
const outputType = Operators['match'].getReturnType(args) & typeHint;
|
||||
assertUniqueInferredType(args, outputType);
|
||||
|
||||
const input = expressionToGlsl(context, args[0]);
|
||||
const fallback = expressionToGlsl(context, args[args.length - 1], outputType);
|
||||
let result = null;
|
||||
for (let i = args.length - 3; i >= 1; i -= 2) {
|
||||
const match = expressionToGlsl(context, args[i]);
|
||||
const output = expressionToGlsl(context, args[i + 1], outputType);
|
||||
result = `(${input} == ${match} ? ${output} : ${result || fallback})`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -260,4 +260,101 @@ describe('ol.style.expressions', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('match operator', function() {
|
||||
let context;
|
||||
|
||||
beforeEach(function() {
|
||||
context = {
|
||||
variables: [],
|
||||
attributes: [],
|
||||
stringLiteralsMap: {}
|
||||
};
|
||||
});
|
||||
|
||||
it('correctly guesses the output type', function() {
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, 'red', 1, 'yellow', 'green']))
|
||||
.to.eql(ValueTypes.STRING | ValueTypes.COLOR);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, 'not_a_color', 1, 'yellow', 'green']))
|
||||
.to.eql(ValueTypes.STRING);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, 'red', 1, 'yellow', 'not_a_color']))
|
||||
.to.eql(ValueTypes.STRING);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, [1, 1, 0], 1, [1, 0, 1], [0, 1, 1]]))
|
||||
.to.eql(ValueTypes.COLOR | ValueTypes.NUMBER_ARRAY);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, [1, 1, 0], 1, [1, 0, 1], 'white']))
|
||||
.to.eql(ValueTypes.COLOR);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, 'red', 1, true, 100]))
|
||||
.to.eql(ValueTypes.NONE);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, false, 1, true, false]))
|
||||
.to.eql(ValueTypes.BOOLEAN);
|
||||
expect(getValueType(['match', ['get', 'attr'], 0, 100, 1, 200, 300]))
|
||||
.to.eql(ValueTypes.NUMBER);
|
||||
});
|
||||
|
||||
it('throws if no single output type could be inferred', function() {
|
||||
let thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['match', ['get', 'attr'], 0, 'red', 1, 'yellow', 'green'], ValueTypes.COLOR);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(false);
|
||||
|
||||
try {
|
||||
expressionToGlsl(context, ['match', ['get', 'attr'], 0, 'red', 1, 'yellow', 'green']);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
|
||||
thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['match', ['get', 'attr'], 0, 'red', 1, 'yellow', 'green'], ValueTypes.NUMBER);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
});
|
||||
|
||||
it('throws if invalid argument count', function() {
|
||||
let thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['match', ['get', 'attr'], 0, true, false, false]);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
|
||||
thrown = false;
|
||||
try {
|
||||
expressionToGlsl(context, ['match', ['get', 'attr'], 0, true]);
|
||||
} catch (e) {
|
||||
thrown = true;
|
||||
}
|
||||
expect(thrown).to.be(true);
|
||||
});
|
||||
|
||||
it('correctly parses the expression (colors)', function() {
|
||||
expect(expressionToGlsl(context, ['match', ['get', 'attr'], 0, 'red', 1, 'yellow', 'white'], ValueTypes.COLOR))
|
||||
.to.eql('(a_attr == 0.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)))');
|
||||
});
|
||||
|
||||
it('correctly parses the expression (strings)', function() {
|
||||
function toGlsl(string) {
|
||||
return stringToGlsl(context, string);
|
||||
}
|
||||
expect(expressionToGlsl(context, ['match', ['get', 'attr'], 10, 'red', 20, 'yellow', 'white'], ValueTypes.STRING))
|
||||
.to.eql(`(a_attr == 10.0 ? ${toGlsl('red')} : (a_attr == 20.0 ? ${toGlsl('yellow')} : ${toGlsl('white')}))`);
|
||||
});
|
||||
|
||||
it('correctly parses the expression (number arrays)', function() {
|
||||
function toGlsl(string) {
|
||||
return stringToGlsl(context, string);
|
||||
}
|
||||
expect(expressionToGlsl(context, ['match', ['get', 'attr'], 'low', [0, 0], 'high', [0, 1], [1, 0]]))
|
||||
.to.eql(`(a_attr == ${toGlsl('low')} ? vec2(0.0, 0.0) : (a_attr == ${toGlsl('high')} ? vec2(0.0, 1.0) : vec2(1.0, 0.0)))`);
|
||||
expect(expressionToGlsl(context, ['match', ['get', 'attr'], 0, [0, 0, 1, 1], 1, [1, 1, 2, 2], 2, [2, 2, 3, 3], [3, 3, 4, 4]], ValueTypes.NUMBER_ARRAY))
|
||||
.to.eql('(a_attr == 0.0 ? vec4(0.0, 0.0, 1.0, 1.0) : (a_attr == 1.0 ? vec4(1.0, 1.0, 2.0, 2.0) : (a_attr == 2.0 ? vec4(2.0, 2.0, 3.0, 3.0) : vec4(3.0, 3.0, 4.0, 4.0))))');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user