mapbox-gl-regex-query
Version:
Query source data using regex
97 lines (81 loc) • 3.44 kB
JavaScript
//adapted from https://github.com/mapbox/mapbox-gl-js/blob/5886421ea9a5ae26797c950ec698be473fe87d17/src/style-spec/feature_filter/index.js
;
module.exports = createFilter;
const types = ['Unknown', 'Point', 'LineString', 'Polygon'];
/**
* Given a filter expressed as nested arrays, return a new function
* that evaluates whether a given feature (with a .properties or .tags property)
* passes its test.
*
* @private
* @param {Array} filter mapbox gl filter
* @returns {Function} filter-evaluating function
*/
function createFilter(filter) {
return new Function('f', `var p = (f && f.properties || {}); return ${compile(filter)}`);
}
function compile(filter) {
if (!filter) return 'true';
const op = filter[0];
if (filter.length <= 1) return op === 'any' ? 'false' : 'true';
const str =
op === '==' ? compileComparisonOp(filter[1], filter[2], '===', false) :
op === '!=' ? compileComparisonOp(filter[1], filter[2], '!==', false) :
op === '<' ||
op === '>' ||
op === '<=' ||
op === '>=' ? compileComparisonOp(filter[1], filter[2], op, true) :
op === '~=' ? compileRegexOp(filter[1], filter[2]) :
op === 'any' ? compileLogicalOp(filter.slice(1), '||') :
op === 'all' ? compileLogicalOp(filter.slice(1), '&&') :
op === 'none' ? compileNegation(compileLogicalOp(filter.slice(1), '||')) :
op === 'in' ? compileInOp(filter[1], filter.slice(2)) :
op === '!in' ? compileNegation(compileInOp(filter[1], filter.slice(2))) :
op === 'has' ? compileHasOp(filter[1]) :
op === '!has' ? compileNegation(compileHasOp(filter[1])) :
'true';
return `(${str})`;
}
function compilePropertyReference(property) {
return property === '$type' ? 'f.type' :
property === '$id' ? 'f.id' :
`p[${JSON.stringify(property)}]`;
}
function compileComparisonOp(property, value, op, checkType) {
const left = compilePropertyReference(property);
const right = property === '$type' ? types.indexOf(value) : JSON.stringify(value);
return (checkType ? `typeof ${left}=== typeof ${right}&&` : '') + left + op + right;
}
function compileRegexOp(property, value) {
const left = compilePropertyReference(property);
const right = value;
return `${'function(left, right) {' +
'if(!left) return false; return left.toString().match(right);' +
'}('}${left}, ${right})`;
}
function compileLogicalOp(expressions, op) {
return expressions.map(compile).join(op);
}
function compileInOp(property, values) {
if (property === '$type') values = values.map((value) => {
return types.indexOf(value);
});
const left = JSON.stringify(values.sort(compare));
const right = compilePropertyReference(property);
if (values.length <= 200) return `${left}.indexOf(${right}) !== -1`;
return `${'function(v, a, i, j) {' +
'while (i <= j) { var m = (i + j) >> 1;' +
' if (a[m] === v) return true; if (a[m] > v) j = m - 1; else i = m + 1;' +
'}' +
'return false; }('}${right}, ${left},0,${values.length - 1})`;
}
function compileHasOp(property) {
return property === '$id' ? '"id" in f' : `${JSON.stringify(property)} in p`;
}
function compileNegation(expression) {
return `!(${expression})`;
}
// Comparison function to sort numbers and strings
function compare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}