s2maps-gpu
Version:
S2 Maps GPU - An open source, high-performance, and GPU-accelerated map engine for rendering large-scale, interactive maps.
110 lines (109 loc) • 4.16 kB
JavaScript
import coalesceField from './coalesceField.js';
/**
* Parse a filter into a filter function
* @param filter - input user defined filter
* @returns a filter function that represents the user defined filter
*/
export default function parseFilter(filter) {
if (filter === undefined)
return () => true;
// case 1: AND
if ('and' in filter) {
const { and } = filter;
const filterLambdas = and.map(parseFilter);
return (properties = {}) => {
for (const filterLambda of filterLambdas) {
if (!filterLambda(properties))
return false;
}
return true;
};
}
else if ('or' in filter) {
// case 2: OR
const { or } = filter;
const filterLambdas = or.map(parseFilter);
return (properties = {}) => {
for (const filterLambda of filterLambdas) {
if (filterLambda(properties))
return true;
}
return false;
};
}
else {
// case 3: Condition
const { key, comparator, value } = filter;
const filterLambda = parseFilterCondition(comparator, value);
return (properties = {}) => {
return filterLambda(coalesceField(key, properties, true), properties);
};
}
}
/**
* Parse a filter condition
* Note: We disable the eslint rule here because we want to allow the use of
* `==` and `!=` instead of `===` and `!==` because we want to allow
* comparasons between strings and numbers.
* @param comparator - comparator operator
* @param value - input value
* @returns Condition function result
*/
function parseFilterCondition(comparator, value) {
// manage multiple comparators
if (comparator === '==')
return (input, properties) =>
// eslint-disable-next-line eqeqeq
input == buildValue(value, properties);
// ['class', '==', 'ocean'] OR ['elev', '==', 50]
else if (comparator === '!=')
return (input, properties) =>
// eslint-disable-next-line eqeqeq
input != buildValue(value, properties);
// ['class', '!=', 'ocean'] OR ['elev', '!=', 50]
else if (comparator === '>')
return (input, properties) => input > buildValue(value, properties);
// ['elev', '>', 50]
else if (comparator === '>=')
return (input, properties) => input >= buildValue(value, properties);
// ['elev', '>=', 50]
else if (comparator === '<')
return (input, properties) => input < buildValue(value, properties);
// ['elev', '<', 50]
else if (comparator === '<=')
return (input, properties) => input <= buildValue(value, properties);
// ['elev', '<=', 50]
else if (comparator === 'has')
return (input, properties) => Array.isArray(value) || typeof value === 'string'
? buildValue(value, properties).includes(input)
: false;
// ['class', 'has', ['ocean', 'river']] OR ['elev', 'in', [2, 3, 4, 5]]
else if (comparator === '!has')
return (input, properties) => Array.isArray(value) || typeof value === 'string'
? !buildValue(value, properties).includes(input)
: true;
// ['class', '!has', ['ocean', 'river']] OR ['elev', '!in', [2, 3, 4, 5]]
else if (comparator === 'in')
return (input) => Array.isArray(input) && typeof value === typeof input[0]
? input.includes(value)
: false;
// ['elev', 'in', 50]
else if (comparator === '!in')
return (input) => Array.isArray(input) && typeof value === typeof input[0]
? !input.includes(value)
: true;
// ['class', '!in', 'ocean'] OR ['elev', '!in', 50]
else
return () => false;
}
/**
* Build a value from a string or an array of strings
* @param value - input value
* @param properties - properties to coalesce
* @returns the built value
*/
function buildValue(value, properties = {}) {
if (typeof value === 'string' || (Array.isArray(value) && typeof value[0] === 'string'))
return coalesceField(value, properties);
return value;
}