diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63e92b661..969a78b60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## UNRELEASED
### Fixed
+- Fix `value`, `eval()` and `getLegendData()` for binary operations
- Support mixed multi geometries in GeoJSON: LineString & MultiLineString, Polygon & MultiPolygon
## [1.3.1] 2019-06-17
diff --git a/debug/advanced/bivariate-legends.html b/debug/advanced/bivariate-legends.html
new file mode 100644
index 000000000..e979d4c2e
--- /dev/null
+++ b/debug/advanced/bivariate-legends.html
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/client/windshaft-filtering.js b/src/client/windshaft-filtering.js
index 246bc7c85..70f04a701 100644
--- a/src/client/windshaft-filtering.js
+++ b/src/client/windshaft-filtering.js
@@ -1,4 +1,11 @@
-import { And, Or, Equals, NotEquals, LessThan, LessThanOrEqualTo, GreaterThan, GreaterThanOrEqualTo } from '../renderer/viz/expressions/binary';
+import GreaterThan from '../renderer/viz/expressions/binary/GreaterThan';
+import GreaterThanOrEqualTo from '../renderer/viz/expressions/binary/GreaterThanOrEqualTo';
+import LessThan from '../renderer/viz/expressions/binary/LessThan';
+import LessThanOrEqualTo from '../renderer/viz/expressions/binary/LessThanOrEqualTo';
+import And from '../renderer/viz/expressions/binary/And';
+import Or from '../renderer/viz/expressions/binary/Or';
+import Equals from '../renderer/viz/expressions/binary/Equals';
+import NotEquals from '../renderer/viz/expressions/binary/NotEquals';
import { In, Nin } from '../renderer/viz/expressions/belongs';
import Between from '../renderer/viz/expressions/between';
import Property from '../renderer/viz/expressions/basic/property';
diff --git a/src/interactivity/featureVizProperty.js b/src/interactivity/featureVizProperty.js
index 115c91816..7349ea375 100644
--- a/src/interactivity/featureVizProperty.js
+++ b/src/interactivity/featureVizProperty.js
@@ -22,6 +22,17 @@ export default class FeatureVizProperty {
}
get value () {
- return this._viz[this._propertyName].eval(this._properties);
+ return this._viz[this._propertyName].value;
+ }
+
+ eval (...properties) {
+ const props = [];
+ properties.forEach((property) => {
+ const prop = {};
+ prop[property] = this._properties[property];
+ props.push(prop);
+ });
+
+ return this._viz[this._propertyName].eval(props);
}
}
diff --git a/src/renderer/viz/expressions.js b/src/renderer/viz/expressions.js
index ff672880d..a51ddbd99 100644
--- a/src/renderer/viz/expressions.js
+++ b/src/renderer/viz/expressions.js
@@ -451,20 +451,21 @@ import { Nin } from './expressions/belongs';
import Between from './expressions/between';
-import { Mul } from './expressions/binary';
-import { Div } from './expressions/binary';
-import { Add } from './expressions/binary';
-import { Sub } from './expressions/binary';
-import { Mod } from './expressions/binary';
-import { Pow } from './expressions/binary';
-import { GreaterThan } from './expressions/binary';
-import { GreaterThanOrEqualTo } from './expressions/binary';
-import { LessThan } from './expressions/binary';
-import { LessThanOrEqualTo } from './expressions/binary';
-import { Equals } from './expressions/binary';
-import { NotEquals } from './expressions/binary';
-import { Or } from './expressions/binary';
-import { And } from './expressions/binary';
+// Binary Operations
+import Add from './expressions/binary/Add';
+import And from './expressions/binary/And';
+import Div from './expressions/binary/Div';
+import Equals from './expressions/binary/Equals';
+import GreaterThan from './expressions/binary/GreaterThan';
+import GreaterThanOrEqualTo from './expressions/binary/GreaterThanOrEqualTo';
+import LessThan from './expressions/binary/LessThan';
+import LessThanOrEqualTo from './expressions/binary/LessThanOrEqualTo';
+import Mod from './expressions/binary/Mod';
+import Mul from './expressions/binary/Mul';
+import NotEquals from './expressions/binary/NotEquals';
+import Or from './expressions/binary/Or';
+import Pow from './expressions/binary/Pow';
+import Sub from './expressions/binary/Sub';
import Blend from './expressions/blend';
diff --git a/src/renderer/viz/expressions/Ramp.js b/src/renderer/viz/expressions/Ramp.js
index ce7142c3c..2e6f37d75 100644
--- a/src/renderer/viz/expressions/Ramp.js
+++ b/src/renderer/viz/expressions/Ramp.js
@@ -129,6 +129,7 @@ export default class Ramp extends BaseExpression {
super({ input, palette });
this.palette = palette;
this.others = others;
+ this.type = palette.type;
this._defaultOthers = others === DEFAULT_RAMP_OTHERS;
}
diff --git a/src/renderer/viz/expressions/basic/List.js b/src/renderer/viz/expressions/basic/List.js
index e08fd9930..9ba0d44e3 100644
--- a/src/renderer/viz/expressions/basic/List.js
+++ b/src/renderer/viz/expressions/basic/List.js
@@ -63,6 +63,7 @@ export default class List extends Base {
super(elems);
this.elems = elems;
+ this.type = elems[0].type;
}
get value () {
diff --git a/src/renderer/viz/expressions/binary.js b/src/renderer/viz/expressions/binary.js
deleted file mode 100644
index 084f950ed..000000000
--- a/src/renderer/viz/expressions/binary.js
+++ /dev/null
@@ -1,525 +0,0 @@
-import { number } from '../expressions';
-import { implicitCast, checkMaxArguments } from './utils';
-import BaseExpression from './base';
-import CartoValidationError, { CartoValidationErrorTypes } from '../../../errors/carto-validation-error';
-
-// Each binary expression can have a set of the following signatures (OR'ed flags)
-const UNSUPPORTED_SIGNATURE = 0;
-const NUMBERS_TO_NUMBER = 1;
-const NUMBER_AND_COLOR_TO_COLOR = 2;
-const COLORS_TO_COLOR = 4;
-const CATEGORIES_TO_NUMBER = 8;
-const IMAGES_TO_IMAGE = 16;
-
-/**
- * Multiply two numeric expressions.
- *
- * @param {Number|Color} x - First value to multiply
- * @param {Number|Color} y - Second value to multiply
- * @return {Number|Color} Result of the multiplication
- *
- * @example
Number multiplication.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * width: s.mul(5, 5) // 25
- * });
- *
- * @example
Number multiplication. (String)
- * const viz = new carto.Viz(`
- * width: 5 * 5 // Equivalent to mul(5, 5)
- * `);
- *
- * @memberof carto.expressions
- * @name mul
- * @function
- * @api
- */
-export const Mul = genBinaryOp('mul',
- NUMBERS_TO_NUMBER | NUMBER_AND_COLOR_TO_COLOR | COLORS_TO_COLOR | IMAGES_TO_IMAGE,
- (x, y) => x * y,
- (x, y) => `(${x} * ${y})`
-);
-
-/**
- * Divide two numeric expressions.
- *
- * @param {Number|Color} numerator - Numerator of the division
- * @param {Number|Color} denominator - Denominator of the division
- * @return {Number|Color} Result of the division
- *
- * @example
Number division.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * width: s.div(10, 2) // 5
- * });
- *
- * @example
Number division. (String)
- * const viz = new carto.Viz(`
- * width: 10 / 2 // Equivalent to div(10, 2)
- * `);
- *
- * @memberof carto.expressions
- * @name div
- * @function
- * @api
- */
-export const Div = genBinaryOp('div',
- NUMBERS_TO_NUMBER | NUMBER_AND_COLOR_TO_COLOR | COLORS_TO_COLOR | IMAGES_TO_IMAGE,
- (x, y) => x / y,
- (x, y) => `(${x} / ${y})`
-);
-
-/**
- * Add two numeric expressions.
- *
- * @param {Number|Color} x - First value to add
- * @param {Number|Color} y - Second value to add
- * @return {Number|Color} Result of the addition
- *
- * @example
Number addition.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * width: s.add(10, 2) // 12
- * });
- *
- * @example
Number addition. (String)
- * const viz = new carto.Viz(`
- * width: 10 + 2 // Equivalent to add(10, 2)
- * `);
- *
- * @memberof carto.expressions
- * @name add
- * @function
- * @api
- */
-export const Add = genBinaryOp('add',
- NUMBERS_TO_NUMBER | COLORS_TO_COLOR | IMAGES_TO_IMAGE,
- (x, y) => x + y,
- (x, y) => `(${x} + ${y})`
-);
-
-/**
- * Substract two numeric expressions.
- *
- * @param {Number|Color} minuend - The minuend of the subtraction
- * @param {Number|Color} subtrahend - The subtrahend of the subtraction
- * @return {Number|Color} Result of the substraction
- *
- * @example
Number subtraction.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * width: s.sub(10, 2) // 8
- * });
- *
- * @example
Number subtraction. (String)
- * const viz = new carto.Viz(`
- * width: 10 - 2 // Equivalent to sub(10, 2)
- * `);
- *
- * @memberof carto.expressions
- * @name sub
- * @function
- * @api
- */
-export const Sub = genBinaryOp('sub',
- NUMBERS_TO_NUMBER | COLORS_TO_COLOR | IMAGES_TO_IMAGE,
- (x, y) => x - y,
- (x, y) => `(${x} - ${y})`
-);
-
-/**
- * Modulus of two numeric expressions, mod returns a numeric expression with the value of x modulo y. This is computed as x - y * floor(x/y).
- *
- * @param {Number} x - First value of the modulus
- * @param {Number} y - Second value of the modulus
- * @return {Number} Result of the modulus
- *
- * @example
Number modulus.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * width: s.mod(10, 6) // 4
- * });
- *
- * @example
Number modulus. (String)
- * const viz = new carto.Viz(`
- * width: 10 % 6 // Equivalent to mod(10, 6)
- * `);
- *
- * @memberof carto.expressions
- * @name mod
- * @function
- * @api
- */
-export const Mod = genBinaryOp('mod',
- NUMBERS_TO_NUMBER,
- (x, y) => x % y,
- (x, y) => `mod(${x}, ${y})`
-);
-
-/**
- * Compute the base to the exponent power, return a numeric expression with the value of the first parameter raised to the power of the second.
- * The result is undefined if x<0 or if x=0 and y≤0.
- *
- * @param {Number} base - Base of the power
- * @param {Number} exponent - Exponent of the power
- * @return {Number} Result of the power
- *
- * @example
Number power.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * width: s.pow(2, 3) // 8
- * });
- *
- * @example
Number power. (String)
- * const viz = new carto.Viz(`
- * width: 2 ^ 3 // Equivalent to pow(2, 3)
- * `);
- *
- * @memberof carto.expressions
- * @name pow
- * @function
- * @api
- */
-export const Pow = genBinaryOp('pow',
- NUMBERS_TO_NUMBER,
- (x, y) => Math.pow(x, y),
- (x, y) => `pow(${x}, ${y})`
-);
-
-/**
- * Compare if x is greater than y.
- *
- * This returns a numeric expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number} x - Firt value of the comparison
- * @param {Number} y - Firt value of the comparison
- * @return {Number} Result of the comparison: 0 or 1
- *
- * @example
Compare two numbers to show only elements with price greater than 30.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * filter: s.gt(s.prop('price'), 30)
- * });
- *
- * @example
Compare two numbers to show only elements with price greater than 30. (String)
- * const viz = new carto.Viz(`
- * filter: $price > 30 // Equivalent to gt($price, 30)
- * `);
- *
- * @memberof carto.expressions
- * @name gt
- * @function
- * @api
- */
-export const GreaterThan = genBinaryOp('greaterThan',
- NUMBERS_TO_NUMBER,
- (x, y) => x > y ? 1 : 0,
- (x, y) => `(${x}>${y}? 1.:0.)`
-);
-
-/**
- * Compare if x is greater than or equal to y.
- *
- * This returns a numeric expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number} x - Firt value of the comparison
- * @param {Number} y - Second value of the comparison
- * @return {Number} Result of the comparison: 0 or 1
- *
- * @example
Compare two numbers to show only elements with price greater than or equal to 30.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * filter: s.gte(s.prop('price'), 30)
- * });
- *
- * @example
Compare two numbers to show only elements with price greater than or equal to 30. (String)
- * const viz = new carto.Viz(`
- * filter: $price >= 30 // Equivalent to gte($price, 30)
- * `);
- *
- * @memberof carto.expressions
- * @name gte
- * @function
- * @api
- */
-export const GreaterThanOrEqualTo = genBinaryOp('greaterThanOrEqualTo',
- NUMBERS_TO_NUMBER,
- (x, y) => x >= y ? 1 : 0,
- (x, y) => `(${x}>=${y}? 1.:0.)`
-);
-
-/**
- * Compare if x is lower than y.
- *
- * This returns a numeric expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number} x - Firt value of the comparison
- * @param {Number} y - Second value of the comparison
- * @return {Number} Result of the comparison: 0 or 1
- *
- * @example
Compare two numbers to show only elements with price less than 30.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * filter: s.lt(s.prop('price'), 30)
- * });
- *
- * @example
Compare two numbers to show only elements with price less than 30. (String)
- * const viz = new carto.Viz(`
- * filter: $price < 30 // Equivalent to lt($price, 30)
- * `);
- *
- * @memberof carto.expressions
- * @name lt
- * @function
- * @api
- */
-export const LessThan = genBinaryOp('lessThan',
- NUMBERS_TO_NUMBER,
- (x, y) => x < y ? 1 : 0,
- (x, y) => `(${x}<${y}? 1.:0.)`
-);
-
-/**
- * Compare if x is lower than or equal to y.
- *
- * This returns a numeric expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number} x - Firt value of the comparison
- * @param {Number} y - Second value of the comparison
- * @return {Number} Result of the comparison: 0 or 1
- *
- * @example
Compare two numbers to show only elements with price less than or equal to 30.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * filter: s.lte(s.prop('price'), 30)
- * });
- *
- * @example
Compare two numbers to show only elements with price less than or equal to 30. (String)
- * const viz = new carto.Viz(`
- * filter: $price <= 30 // Equivalent to lte($price, 30)
- * `);
- *
- * @memberof carto.expressions
- * @name lte
- * @function
- * @api
- */
-export const LessThanOrEqualTo = genBinaryOp('lessThanOrEqualTo',
- NUMBERS_TO_NUMBER,
- (x, y) => x <= y ? 1 : 0,
- (x, y) => `(${x}<=${y}? 1.:0.)`
-);
-
-/**
- * Compare if x is equal to a y.
- *
- * This returns a numeric expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number|Category} x - Firt value of the comparison
- * @param {Number|Category} y - Second value of the comparison
- * @return {Number} Result of the comparison: 0 or 1
- *
- * @example
Compare two numbers to show only elements with price equal to 30.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * filter: s.eq(s.prop('price'), 30)
- * });
- *
- * @example
Compare two numbers to show only elements with price equal to 30. (String)
- * const viz = new carto.Viz(`
- * filter: $price == 30 // Equivalent to eq($price, 30)
- * `);
- *
- * @memberof carto.expressions
- * @name eq
- * @function
- * @api
- */
-export const Equals = genBinaryOp('equals',
- NUMBERS_TO_NUMBER | CATEGORIES_TO_NUMBER,
- (x, y) => x === y ? 1 : 0,
- (x, y) => `(${x}==${y}? 1.:0.)`
-);
-
-/**
- * Compare if x is different than y.
- *
- * This returns a number expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number|Category} x - Firt value of the comparison
- * @param {Number|Category} y - Second value of the comparison
- * @return {Number} Result of the comparison: 0 or 1
- *
- * @example
Compare two numbers to show only elements with price not equal to 30.
- * const s = carto.expressions;
- * const viz = new carto.Viz({
- * filter: s.neq(s.prop('price'), 30);
- * });
- *
- * @example
Compare two numbers to show only elements with price not equal to 30. (String)
- * const viz = new carto.Viz(`
- * filter: $price != 30 // Equivalent to neq($price, 30)
- * `);
- *
- * @memberof carto.expressions
- * @name neq
- * @function
- * @api
- */
-export const NotEquals = genBinaryOp('notEquals',
- NUMBERS_TO_NUMBER | CATEGORIES_TO_NUMBER,
- (x, y) => x !== y ? 1 : 0,
- (x, y) => `(${x}!=${y}? 1.:0.)`
-);
-
-/**
- * Perform a binary OR between two numeric expressions.
- * If the numbers are different from 0 or 1 this performs a [fuzzy or operation](https://en.wikipedia.org/wiki/Fuzzy_logic#Fuzzification).
- * This fuzzy behavior will allow transitions to work in a continuos, non-discrete, fashion.
- *
- * This returns a number expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number} x - First value of the expression
- * @param {Number} y - Second value of the expression
- * @return {Number} Result of the expression
- *
- * @example
Show only elements with price < 30 OR price > 1000.
Show only elements with price < 30 OR price > 1000. (String)
- * const viz = new carto.Viz(`
- * filter: $price < 30 or $price > 1000 // Equivalent to or(lt($price, 30), gt($price, 1000))
- * `);
- *
- * @memberof carto.expressions
- * @name or
- * @function
- * @api
- */
-export const Or = genBinaryOp('or',
- NUMBERS_TO_NUMBER,
- (x, y) => Math.min(x + y, 1),
- (x, y) => `min(${x} + ${y}, 1.)`
-);
-
-/**
- * Perform a binary AND between two numeric expressions.
- * If the numbers are different from 0 or 1 this performs a [fuzzy and operation](https://en.wikipedia.org/wiki/Fuzzy_logic#Fuzzification).
- * This fuzzy behavior will allow transitions to work in a continuos, non-discrete, fashion.
- *
- * This returns a number expression where 0 means `false` and 1 means `true`.
- *
- * @param {Number} x - First value of the expression
- * @param {Number} y - Second value of the expression
- * @return {Number} Result of the expression
- *
- * @example
Show only elements with price < 30 AND category === 'fruit'.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * width: s.div(10, 2) // 5
+ * });
+ *
+ * @example
Number division. (String)
+ * const viz = new carto.Viz(`
+ * width: 10 / 2 // Equivalent to div(10, 2)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name div
+ * @function
+ * @api
+ */
+export default class Div extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ if (Number.isFinite(a) && Number.isFinite(b)) {
+ return number(a / b);
+ }
+
+ const signatureMethods = {
+ 1: (x, y) => x / y, // NUMBERS_TO_NUMBER
+ 2: _divNumberColor, // NUMBER_AND_COLOR_TO_COLOR
+ 4: _divColors // COLORS_TO_COLOR
+ };
+
+ const glsl = (x, y) => `(${x} / ${y})`;
+
+ a = implicitCast(a);
+ b = implicitCast(b);
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER | NUMBER_AND_COLOR_TO_COLOR | COLORS_TO_COLOR | IMAGES_TO_IMAGE;
+ }
+}
+
+function _divColors (colorA, colorB) {
+ return {
+ r: Math.round(colorA.r / colorB.r),
+ g: Math.round(colorA.g / colorB.g),
+ b: Math.round(colorA.b / colorB.b),
+ a: colorA.a
+ };
+}
+
+function _divNumberColor (valueA, valueB) {
+ const { n, color } = typeof valueA === 'number'
+ ? { n: valueA, color: valueB }
+ : { n: valueB, color: valueA };
+
+ return {
+ r: Math.round(color.r / n),
+ g: Math.round(color.g / n),
+ b: Math.round(color.b / n),
+ a: color.a
+ };
+}
diff --git a/src/renderer/viz/expressions/binary/Equals.js b/src/renderer/viz/expressions/binary/Equals.js
new file mode 100644
index 000000000..3a7fd0ba1
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/Equals.js
@@ -0,0 +1,44 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER, CATEGORIES_TO_NUMBER } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compare if x is equal to a y.
+ *
+ * This returns a numeric expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number|Category} x - Firt value of the comparison
+ * @param {Number|Category} y - Second value of the comparison
+ * @return {Number} Result of the comparison: 0 or 1
+ *
+ * @example
Compare two numbers to show only elements with price equal to 30.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * filter: s.eq(s.prop('price'), 30)
+ * });
+ *
+ * @example
Compare two numbers to show only elements with price equal to 30. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price == 30 // Equivalent to eq($price, 30)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name eq
+ * @function
+ * @api
+ */
+export default class Equals extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x === y ? 1 : 0, // NUMBERS_TO_NUMBER,
+ 8: (x, y) => x === y ? 1 : 0 // CATEGORIES_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `(${x}==${y}? 1.:0.)`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER | CATEGORIES_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/GreaterThan.js b/src/renderer/viz/expressions/binary/GreaterThan.js
new file mode 100644
index 000000000..33b26e26f
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/GreaterThan.js
@@ -0,0 +1,43 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compare if x is greater than y.
+ *
+ * This returns a numeric expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number} x - Firt value of the comparison
+ * @param {Number} y - Firt value of the comparison
+ * @return {Number} Result of the comparison: 0 or 1
+ *
+ * @example
Compare two numbers to show only elements with price greater than 30.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * filter: s.gt(s.prop('price'), 30)
+ * });
+ *
+ * @example
Compare two numbers to show only elements with price greater than 30. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price > 30 // Equivalent to gt($price, 30)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name gt
+ * @function
+ * @api
+ */
+export default class GreaterThan extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x > y ? 1 : 0 // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `(${x}>${y}? 1.:0.)`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/GreaterThanOrEqualTo.js b/src/renderer/viz/expressions/binary/GreaterThanOrEqualTo.js
new file mode 100644
index 000000000..b9bcadcec
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/GreaterThanOrEqualTo.js
@@ -0,0 +1,43 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compare if x is greater than or equal to y.
+ *
+ * This returns a numeric expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number} x - Firt value of the comparison
+ * @param {Number} y - Second value of the comparison
+ * @return {Number} Result of the comparison: 0 or 1
+ *
+ * @example
Compare two numbers to show only elements with price greater than or equal to 30.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * filter: s.gte(s.prop('price'), 30)
+ * });
+ *
+ * @example
Compare two numbers to show only elements with price greater than or equal to 30. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price >= 30 // Equivalent to gte($price, 30)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name gte
+ * @function
+ * @api
+ */
+export default class GreaterThanOrEqualTo extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x >= y ? 1 : 0 // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `(${x}>=${y}? 1.:0.)`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/LessThan.js b/src/renderer/viz/expressions/binary/LessThan.js
new file mode 100644
index 000000000..355f2be0a
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/LessThan.js
@@ -0,0 +1,43 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compare if x is lower than y.
+ *
+ * This returns a numeric expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number} x - Firt value of the comparison
+ * @param {Number} y - Second value of the comparison
+ * @return {Number} Result of the comparison: 0 or 1
+ *
+ * @example
Compare two numbers to show only elements with price less than 30.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * filter: s.lt(s.prop('price'), 30)
+ * });
+ *
+ * @example
Compare two numbers to show only elements with price less than 30. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price < 30 // Equivalent to lt($price, 30)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name lt
+ * @function
+ * @api
+ */
+export default class LessThan extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x < y ? 1 : 0 // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `(${x}<${y}? 1.:0.)`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/LessThanOrEqualTo.js b/src/renderer/viz/expressions/binary/LessThanOrEqualTo.js
new file mode 100644
index 000000000..723a942e9
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/LessThanOrEqualTo.js
@@ -0,0 +1,43 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compare if x is lower than or equal to y.
+ *
+ * This returns a numeric expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number} x - Firt value of the comparison
+ * @param {Number} y - Second value of the comparison
+ * @return {Number} Result of the comparison: 0 or 1
+ *
+ * @example
Compare two numbers to show only elements with price less than or equal to 30.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * filter: s.lte(s.prop('price'), 30)
+ * });
+ *
+ * @example
Compare two numbers to show only elements with price less than or equal to 30. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price <= 30 // Equivalent to lte($price, 30)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name lte
+ * @function
+ * @api
+ */
+export default class LessThanOrEqualTo extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x <= y ? 1 : 0 // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `(${x}<=${y}? 1.:0.)`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/Mod.js b/src/renderer/viz/expressions/binary/Mod.js
new file mode 100644
index 000000000..45002daa2
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/Mod.js
@@ -0,0 +1,44 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+
+import { implicitCast, checkMaxArguments } from '../utils';
+
+/**
+ * Modulus of two numeric expressions, mod returns a numeric expression with the value of x module y. This is computed as x - y * floor(x/y).
+ *
+ * @param {Number} x - First value of the modulus
+ * @param {Number} y - Second value of the modulus
+ * @return {Number} Result of the modulus
+ *
+ * @example
Number modulus.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * width: s.mod(10, 6) // 4
+ * });
+ *
+ * @example
Number modulus. (String)
+ * const viz = new carto.Viz(`
+ * width: 10 % 6 // Equivalent to mod(10, 6)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name mod
+ * @function
+ * @api
+ */
+export default class Mod extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x % y // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `mod(${x}, ${y})`;
+
+ a = implicitCast(a);
+ b = implicitCast(b);
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/Mul.js b/src/renderer/viz/expressions/binary/Mul.js
new file mode 100644
index 000000000..d8ee3692b
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/Mul.js
@@ -0,0 +1,71 @@
+import {
+ BinaryOperation,
+ NUMBERS_TO_NUMBER,
+ NUMBER_AND_COLOR_TO_COLOR,
+ COLORS_TO_COLOR,
+ IMAGES_TO_IMAGE
+} from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Multiply two numeric expressions.
+ *
+ * @param {Number|Color} x - First value to multiply
+ * @param {Number|Color} y - Second value to multiply
+ * @return {Number|Color} Result of the multiplication
+ *
+ * @example
Number multiplication.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * width: s.mul(5, 5) // 25
+ * });
+ *
+ * @example
Number multiplication. (String)
+ * const viz = new carto.Viz(`
+ * width: 5 * 5 // Equivalent to mul(5, 5)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name mul
+ * @function
+ * @api
+ */
+export default class Mul extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x * y, // NUMBERS_TO_NUMBER
+ 2: _mulNumberColor, // NUMBER_AND_COLOR_TO_COLOR
+ 4: _mulColors // COLORS_TO_COLOR
+ };
+
+ const glsl = (x, y) => `(${x} * ${y})`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER | NUMBER_AND_COLOR_TO_COLOR | COLORS_TO_COLOR | IMAGES_TO_IMAGE;
+ }
+}
+
+function _mulColors (colorA, colorB) {
+ return {
+ r: Math.round(colorA.r * colorB.r / 255),
+ g: Math.round(colorA.g * colorB.g / 255),
+ b: Math.round(colorA.b * colorB.b / 255),
+ a: colorA.a
+ };
+}
+
+function _mulNumberColor (valueA, valueB) {
+ const { n, color } = typeof valueA === 'number'
+ ? { n: valueA, color: valueB }
+ : { n: valueB, color: valueA };
+
+ return {
+ r: Math.round(n * color.r / 255),
+ g: Math.round(n * color.g / 255),
+ b: Math.round(n * color.b / 255),
+ a: color.a
+ };
+}
diff --git a/src/renderer/viz/expressions/binary/NotEquals.js b/src/renderer/viz/expressions/binary/NotEquals.js
new file mode 100644
index 000000000..b6cebdf0f
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/NotEquals.js
@@ -0,0 +1,44 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER, CATEGORIES_TO_NUMBER } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compare if x is different than y.
+ *
+ * This returns a number expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number|Category} x - Firt value of the comparison
+ * @param {Number|Category} y - Second value of the comparison
+ * @return {Number} Result of the comparison: 0 or 1
+ *
+ * @example
Compare two numbers to show only elements with price not equal to 30.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * filter: s.neq(s.prop('price'), 30);
+ * });
+ *
+ * @example
Compare two numbers to show only elements with price not equal to 30. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price != 30 // Equivalent to neq($price, 30)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name neq
+ * @function
+ * @api
+ */
+export default class NotEquals extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => x !== y ? 1 : 0, // NUMBERS_TO_NUMBER
+ 8: (x, y) => x !== y ? 1 : 0
+ };
+
+ const glsl = (x, y) => `(${x}!=${y}? 1.:0.)`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER | CATEGORIES_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/Or.js b/src/renderer/viz/expressions/binary/Or.js
new file mode 100644
index 000000000..2d5a57001
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/Or.js
@@ -0,0 +1,51 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+
+import { implicitCast, checkMaxArguments } from '../utils';
+
+/**
+ * Perform a binary OR between two numeric expressions.
+ * If the numbers are different from 0 or 1 this performs a [fuzzy or operation](https://en.wikipedia.org/wiki/Fuzzy_logic#Fuzzification).
+ * This fuzzy behavior will allow transitions to work in a continuos, non-discrete, fashion.
+ *
+ * This returns a number expression where 0 means `false` and 1 means `true`.
+ *
+ * @param {Number} x - First value of the expression
+ * @param {Number} y - Second value of the expression
+ * @return {Number} Result of the expression
+ *
+ * @example
Show only elements with price < 30 OR price > 1000.
Show only elements with price < 30 OR price > 1000. (String)
+ * const viz = new carto.Viz(`
+ * filter: $price < 30 or $price > 1000 // Equivalent to or(lt($price, 30), gt($price, 1000))
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name or
+ * @function
+ * @api
+ */
+export default class Or extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => Math.min(x + y, 1) // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `min(${x} + ${y}, 1.)`;
+
+ a = implicitCast(a);
+ b = implicitCast(b);
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/Pow.js b/src/renderer/viz/expressions/binary/Pow.js
new file mode 100644
index 000000000..02049af8c
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/Pow.js
@@ -0,0 +1,41 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER } from './BinaryOperation';
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Compute the base to the exponent power, return a numeric expression with the value of the first parameter raised to the power of the second.
+ * The result is undefined if x<0 or if x=0 and y≤0.
+ *
+ * @param {Number} base - Base of the power
+ * @param {Number} exponent - Exponent of the power
+ * @return {Number} Result of the power
+ *
+ * @example
Number power.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * width: s.pow(2, 3) // 8
+ * });
+ *
+ * @example
Number power. (String)
+ * const viz = new carto.Viz(`
+ * width: 2 ^ 3 // Equivalent to pow(2, 3)
+ * `);
+ *
+ * @memberof carto.expressions
+ * @name pow
+ * @function
+ * @api
+ */
+export default class Pow extends BinaryOperation {
+ constructor (a, b) {
+ checkMaxArguments(arguments, 2);
+
+ const signatureMethods = {
+ 1: (x, y) => Math.pow(x, y) // NUMBERS_TO_NUMBER
+ };
+
+ const glsl = (x, y) => `pow(${x}, ${y})`;
+
+ super(a, b, signatureMethods, glsl);
+ this.allowedSignature = NUMBERS_TO_NUMBER;
+ }
+}
diff --git a/src/renderer/viz/expressions/binary/Sub.js b/src/renderer/viz/expressions/binary/Sub.js
new file mode 100644
index 000000000..4f114335a
--- /dev/null
+++ b/src/renderer/viz/expressions/binary/Sub.js
@@ -0,0 +1,51 @@
+import { BinaryOperation, NUMBERS_TO_NUMBER, COLORS_TO_COLOR, IMAGES_TO_IMAGE } from './BinaryOperation';
+
+import { checkMaxArguments } from '../utils';
+
+/**
+ * Substract two numeric expressions.
+ *
+ * @param {Number|Color} minuend - The minuend of the subtraction
+ * @param {Number|Color} subtrahend - The subtrahend of the subtraction
+ * @return {Number|Color} Result of the substraction
+ *
+ * @example
Number subtraction.
+ * const s = carto.expressions;
+ * const viz = new carto.Viz({
+ * width: s.sub(10, 2) // 8
+ * });
+ *
+ * @example