Expression
@nextgis/expression is a versatile library designed for executing actions on objects based on expressions inspired by the syntax of Maplibre Style expressions. These expressions are JSON-serializable, making them perfect for configurations, data processing, or any other kind of object manipulation.
Features
- JSON-serializable Expressions: Represent complex logic and operations in a portable and easily shareable format.
- Zero Dependencies: Built to be completely standalone without relying on external packages.
- Versatility: Ability to perform various operations on objects directly in JavaScript, from simple retrieval to complex computations.
- Easy Integration: Designed with a simple API for hassle-free integration into any project.
Installation
$ npm install --save-dev @nextgis/expression
$ yarn add @nextgis/expression
Usage
Expressions are like little command lists inside arrays. The first item in this list tells you what action to do, like "add" or "multiply". The items after that are the things you're acting on.
For example, to multiply 5 by 3:
const myExpression = ["*", 5, 3];
To find out what this equals, use the evaluate function:
import { evaluate } from '@nextgis/expression';
const answer = evaluate(["*", 5, 3]);
console.log(answer);
You can even put expressions inside other expressions:
const biggerExpression = ["+", ["*", 5, 3], 4];
const biggerAnswer = evaluate(biggerExpression);
console.log(biggerAnswer);
When we need to retrieve data stored in an object, the second argument in evaluate becomes invaluable.
Imagine you have some data:
const lakeData = {
name: "Baikal",
depth: 1642,
volume: "23,600 km³",
islands: ["Olkhon", "Big Ushkany", "Small Ushkany"]
};
You can use expressions to access or manipulate parts of this data.
To fetch the name of the lake:
const nameExpression = ["get", "name"];
Execute this with evaluate, passing in both the expression and the data:
const lakeName = evaluate(["get", "name"], lakeData);
console.log(lakeName);
Want the first island from the "islands" array? Do this:
const islandExpression = ["at", 0, ["get", "islands"]];
const firstIsland = evaluate(islandExpression, lakeData);
console.log(firstIsland);
To visually represent data, sometimes we might want to assign colors based on numerical values. For instance, we can use interpolation to determine a color for Lake Baikal based on its depth.
Let's decide on a simple color gradient:
Up to 500 meters: Light Blue (#ADD8E6)
At 1642 meters (Baikal's maximum depth): Deep Blue (#00008B)
const colorExpression = [
"interpolate", ["linear"], ["get", "depth"],
500, "#ADD8E6",
1642, "#00008B"
];
const derivedColor = evaluate(colorExpression, lakeData);
console.log(derivedColor);
Let's move on to exploring all possible expression types and examples of their use.
Types Expressions
Expressions under the "Types" category are designed to work with and convert data types. Here are some of the available types expressions:
'literal'
API
The literal expression provides a way to explicitly label a value as a raw constant, ensuring that it's interpreted as a literal instead of an expression. This can be especially useful in scenarios where there might be ambiguity.
import { evaluate } from '@nextgis/expression';
const literalExpression = ['literal', [10, 20, 30]];
console.log(evaluate(literalExpression));
'array'
API
For defining arrays and specifying types within arrays.
Basic Array
import { evaluate } from '@nextgis/expression';
const obj = { array: [1,2,3] }
console.log(evaluate(['array', ['get', 'array']], obj));
Typed Array
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['array', 'number', ['get', 'array']]));
Fixed Length Typed Array
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['array', 'string', 2, ["literal", ["Hello", "World"]]]));
'boolean', 'literal', 'number', 'object', 'string'
These are basic type expressions to directly specify a particular type of data.
Example:
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['boolean', true]));
console.log(evaluate(['number', 42]));
console.log(evaluate(['object', ['literal', { key: "value" }]], obj));
console.log(evaluate(['string', "Hello World"]));
Fallbacks and Errors
If multiple values are provided for type expressions like 'boolean', each one is evaluated in order until a result of the specified type is obtained. If none of the provided values match the specified type, the expression results in an error.
console.log(evaluate(['boolean', "not a boolean", "still not a boolean", true]));
try {
console.log(evaluate(['boolean', "not a boolean", "still not a boolean", 42]));
} catch (error) {
console.error(error.message);
}
'to-boolean', 'to-number', 'to-string'
These expressions allow for type conversions.
const obj = {
value1: "true",
value2: "hello",
value3: 100
};
console.log(evaluate(['to-boolean', ["get", "value1"], true], obj));
console.log(evaluate(['to-number', ["get", "value2"], ["get", "value3"]], obj));
console.log(evaluate(['to-string', ["get", "value3"]], obj));
'typeof'
This expression returns the type of the given value.
const obj = {
value: [1, 2, 3]
};
console.log(evaluate(['typeof', ["get", "value"]], obj));
Lookup Expressions
The Lookup category of expressions allows users to access and modify specific parts of the data. This category includes methods to extract a value by its key or index, determine if an object has a certain property, find out the length of a string or array, and many others.
'at'
API
Fetches a value from an array using an index.
import { evaluate } from '@nextgis/expression';
const obj = {
values: [10, 20, 30, 40]
};
console.log(evaluate(['at', 2, ['get', 'values']], obj));
'get'
API
Retrieves the value of a property from an object.
const obj = {
foo: 'bar',
key: 'foo',
nested: { key: 'value' },
};
console.log(evaluate(['get', 'foo'], obj))
console.log(evaluate(['get', 'nested'], obj))
console.log(evaluate(['get', 'key', ['get', 'nested']], obj))
console.log(evaluate(['get', 'nonexistent'], obj))
'has'
API
console.log(evaluate(['has', 'width'], { type: 'River', width: 30 }));
console.log(evaluate(['has', 'width', ['literal', { type: 'River', width: 30 }]]));
'length'
API
Returns the length of a string or an array.
console.log(evaluate(['length', ['get', 'name']], { name: 'NextGIS' }));
'in'
API
Checks if an element exists in an array.
console.log(evaluate(['in', 'blue', ['get', 'colors']], {
colors: ['red', 'blue', 'green']
}));
'index-of'
API
Returns the index of the first occurrence of a substring or an array element.
const obj = {
colors: ['red', 'blue', 'green']
};
console.log(evaluate(['index-of', 'blue', ['get', 'colors']], obj));
'slice'
API
Returns a subsection of a string or an array.
const obj = {
animals: ['dog', 'cat', 'bird', 'fish']
};
console.log(evaluate(['slice', ['get', 'animals'], 1, 3], obj));
Decision Expressions
The Decision category provides simple tools to make comparisons and decisions based on your data. These expressions help you check equality, make logic-based choices, and more.
'!'
API
Returns the logical negation of the input.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['!', true]));
'!='
API
Checks if two values are not equal.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['!=', ['get', 'type'], 'River'], { type: 'Stream' }));
'<'
API
Checks if the first value is less than the second.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['<', ['get', 'length'], 100], { length: 80 }));
'<='
API
Checks if the first value is less than or equal to the second.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['<=', ['get', 'length'], 100], { length: 100 }));
'=='
API
Checks if two values are equal.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['==', ['get', 'type'], 'River'], { type: 'River' }));
'>='
API
Checks if the first value is greater than or equal to the second.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['>=', ['get', 'width'], 50], { width: 60 }));
'>'
API
Checks if the first value is greater than the second.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['>', ['get', 'width'], 50], { width: 40 }));
'all'
API
Evaluates multiple conditions and returns true if all of them are true.
import { evaluate } from '@nextgis/expression';
console.log(evaluate(['all', ['>', ['get', 'width'], 50], ['==', ['get', 'type'], 'River']], { width: 60, type: 'River' }));
'any'
API
Evaluates multiple conditions and returns true if at least one of them is true.
import { evaluate } from '@nextgis/expression';
console.log(evaluate([
'any',
['>', ['get', 'width'], 100],
['==', ['get', 'type'], 'Stream']
], { width: 80, type: 'Stream' }));
'case'
API
Evaluates each pair of conditions and values sequentially and returns the output of the first true condition. If none are true, it returns the value of the optional default case.
import { evaluate } from '@nextgis/expression';
const obj = {
two: 2,
str: 'Foo Bar',
};
evaluate([
'case',
['==', ['get', 'two'], 2],
'matches two',
['==', ['get', 'str'], 'Not Foo Bar'],
'matches string',
'fallback',
],
obj,
)
'match'
API
Matches an input with a series of cases, returning the output associated with the first matching case. If there's no match, a default value is returned.
import { evaluate } from '@nextgis/expression';
const obj = { shape: 'circle' };
console.log(evaluate([
'match',
['get', 'shape'],
'square', 'It is a square',
'circle', 'It is a circle',
'Unknown shape'],
obj));
Interpolation Expressions
Interpolation expressions allow you to create smooth transitions between values based on your input data. They can be especially useful when visualizing data gradients or when you want to transition between two values.
'step'
API
The step expression creates a stepped, piecewise-constant function. It's mainly used when you want to categorize your data into specific bins.
import { evaluate } from '@nextgis/expression';
const prop = { five: 5, one: 1 };
console.log(evaluate(['step', ['get', 'five'], 0, 4, 'low', 6, 'medium', 8, 'high'], prop));
console.log(evaluate(['step', 6, 0, 4, 'low', 6, 'medium', 8, 'high'], prop));
console.log(evaluate(['step', 9, 0, 4, 'low', 6, 'medium', 8, 'high'], prop));
console.log(evaluate(['step', ['get', 'one'], 'low', 6, 'medium', 8, 'high'], prop));
'interpolate'
API
The interpolate expression provides smooth transitions between pairs of input and output values.
Currently, only linear interpolation is supported.
import { evaluate } from '@nextgis/expression';
const prop = { value: 5 };
console.log(evaluate([
'interpolate',
['linear'],
['get', 'value'],
0, 10,
10, 20
], prop));
console.log(evaluate([
'interpolate',
['linear'],
['get', 'value'],
0, '#000000',
10, '#ffffff'
], prop
));
console.log(evaluate([
'interpolate',
['linear'],
['get', 'value'],
0, 'black',
10, 'white'
], prop
));
console.log(evaluate([
'interpolate',
['linear'],
['get', 'value'],
0, 'rgb(0,0,0)',
10, 'rgb(255,255,255)'
], prop));
console.log(evaluate([
'interpolate',
['linear'],
14,
0, 10,
5, 20,
10, 30,
15, 40
]));
Math Expressions
API
The Math category includes a comprehensive set of mathematical operations to perform arithmetic, trigonometric, logarithmic, and other related calculations.
Supported Operations:
- Arithmetic: +, -, *, /, %, ^, abs, ceil, floor, round, max, min, sqrt
- Trigonometric: acos, asin, atan, cos, sin, tan
- Logarithmic and Exponential: ln, ln2, log10, log2, e
import { evaluate } from '@nextgis/expression';
evaluate(['+', 1, 2, 3, 4]);
evaluate(['+', 1, 2, 3, 4]);
evaluate(['sin', ['/', 'pi', 2]]);
Commercial support
Need to fix a bug or add a feature to @nextgis/expression
? We provide custom development and support for this software. Contact us to discuss options!