This work is based on the ol.expr package by @tschaub. It adds a few named expression functions to that package: * INTERSECTS, CONTAINS, DWITHIN, WITHIN (no client-side implementation as yet) * LIKE (like comparison with wildcard, singleChar and escapeChar) * IEQ (case-insensitive equality) * INEQ (case-insensitive non-equality) It also adds a few extra parameters to the existing EXTENT function to be able to deserialize and serialize all info (i.e. projection and property name). Some changes were needed for the GML parser as well: * createGeometry function needed to be public * when parsing Box (GML2) and Envelope (GML3) also parse the srsName * fix up writing for Box and Envelope now that bounds is an array Also added createDocumentFragment function to the XML parser. Implementation is similar to OpenLayers 2. Some addtional notes on the implementation: * PropertyIsBetween was implemented as an ol.expr.Logical with operator ol.expr.LogicalOp.AND and two ol.expr.Comparison instances with operator ol.expr.ComparisonOp.GTE and ol.expr.ComparisonOp.LTE * In OGC Filter And and Or can contain more than 2 sub filters, so this is translated into a hierarchy of ol.expr.Logical
267 lines
7.8 KiB
JavaScript
267 lines
7.8 KiB
JavaScript
goog.provide('ol.expr');
|
|
goog.provide('ol.expr.functions');
|
|
|
|
goog.require('ol.Extent');
|
|
goog.require('ol.Feature');
|
|
goog.require('ol.expr.Call');
|
|
goog.require('ol.expr.Expression');
|
|
goog.require('ol.expr.Identifier');
|
|
goog.require('ol.expr.Parser');
|
|
goog.require('ol.extent');
|
|
goog.require('ol.geom.GeometryType');
|
|
|
|
|
|
/**
|
|
* Evaluate an expression with a feature. The feature attributes will be used
|
|
* as the evaluation scope. The `ol.expr.lib` functions will be used as
|
|
* function scope. The feature itself will be used as the `this` argument.
|
|
*
|
|
* @param {ol.expr.Expression} expr The expression.
|
|
* @param {ol.Feature=} opt_feature The feature.
|
|
* @return {*} The result of the expression.
|
|
*/
|
|
ol.expr.evaluateFeature = function(expr, opt_feature) {
|
|
var result;
|
|
if (goog.isDef(opt_feature)) {
|
|
result = expr.evaluate(
|
|
opt_feature.getAttributes(), ol.expr.lib, opt_feature);
|
|
} else {
|
|
result = expr.evaluate();
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
/**
|
|
* Parse an expression.
|
|
* @param {string} source The expression source (e.g. `'foo + 2'`).
|
|
* @return {ol.expr.Expression} An expression instance that can be
|
|
* evaluated within some scope to provide a value.
|
|
*/
|
|
ol.expr.parse = function(source) {
|
|
var parser = new ol.expr.Parser();
|
|
return parser.parse(source);
|
|
};
|
|
|
|
|
|
/**
|
|
* Register a library function to be used in expressions.
|
|
* @param {string} name The function name (e.g. 'myFunc').
|
|
* @param {function(this:ol.Feature)} func The function to be called in an
|
|
* expression. This function will be called with a feature as the `this`
|
|
* argument when the expression is evaluated in the context of a features.
|
|
*/
|
|
ol.expr.register = function(name, func) {
|
|
ol.expr.lib[name] = func;
|
|
};
|
|
|
|
|
|
/**
|
|
* Determines whether an expression is a call expression that calls one of the
|
|
* `ol.expr.lib` functions.
|
|
*
|
|
* @param {ol.expr.Expression} expr The candidate expression.
|
|
* @return {string|undefined} If the candidate expression is a call to a lib
|
|
* function, the return will be the function name. If not, the return will be
|
|
* `undefined`.
|
|
*/
|
|
ol.expr.isLibCall = function(expr) {
|
|
var name;
|
|
if (expr instanceof ol.expr.Call) {
|
|
var callee = expr.getCallee();
|
|
if (callee instanceof ol.expr.Identifier) {
|
|
name = callee.getName();
|
|
if (!ol.expr.lib.hasOwnProperty(name)) {
|
|
name = undefined;
|
|
}
|
|
}
|
|
}
|
|
return name;
|
|
};
|
|
|
|
|
|
/**
|
|
* Library of well-known functions. These are available to expressions parsed
|
|
* with `ol.expr.parse`.
|
|
*
|
|
* @type {Object.<string, function(...)>}
|
|
*/
|
|
ol.expr.lib = {};
|
|
|
|
|
|
/**
|
|
* Enumeration of library function names.
|
|
*
|
|
* @enum {string}
|
|
*/
|
|
ol.expr.functions = {
|
|
EXTENT: 'extent',
|
|
FID: 'fid',
|
|
GEOMETRY_TYPE: 'geometryType',
|
|
INTERSECTS: 'intersects',
|
|
CONTAINS: 'contains',
|
|
DWITHIN: 'dwithin',
|
|
WITHIN: 'within',
|
|
LIKE: 'like',
|
|
IEQ: 'ieq',
|
|
INEQ: 'ineq'
|
|
};
|
|
|
|
|
|
/**
|
|
* Determine if a feature's extent intersects the provided extent.
|
|
* @param {number} minX Minimum x-coordinate value.
|
|
* @param {number} maxX Maximum x-coordinate value.
|
|
* @param {number} minY Minimum y-coordinate value.
|
|
* @param {number} maxY Maximum y-coordinate value.
|
|
* @param {string=} opt_projection Projection of the extent.
|
|
* @param {string=} opt_attribute Name of the geometry attribute to use.
|
|
* @return {boolean} The provided extent intersects the feature's extent.
|
|
* @this {ol.Feature}
|
|
*/
|
|
ol.expr.lib[ol.expr.functions.EXTENT] = function(minX, maxX, minY, maxY,
|
|
opt_projection, opt_attribute) {
|
|
var intersects = false;
|
|
var geometry = goog.isDef(opt_attribute) ?
|
|
this.getAttributes()[opt_attribute] : this.getGeometry();
|
|
if (geometry) {
|
|
intersects = ol.extent.intersects(geometry.getBounds(),
|
|
[minX, maxX, minY, maxY]);
|
|
}
|
|
return intersects;
|
|
};
|
|
|
|
|
|
/**
|
|
* Determine if the feature identifier matches any of the provided values.
|
|
* @param {...string} var_args Feature identifiers.
|
|
* @return {boolean} The feature's identifier matches one of the given values.
|
|
* @this {ol.Feature}
|
|
*/
|
|
ol.expr.lib[ol.expr.functions.FID] = function(var_args) {
|
|
var matches = false;
|
|
var id = this.getFeatureId();
|
|
if (goog.isDef(id)) {
|
|
for (var i = 0, ii = arguments.length; i < ii; ++i) {
|
|
if (arguments[i] === id) {
|
|
matches = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return matches;
|
|
};
|
|
|
|
|
|
/**
|
|
* Determine if a feature attribute is like the provided value.
|
|
* @param {string} attribute The name of the attribute to test for.
|
|
* @param {string} value The value to test for.
|
|
* @param {string} wildCard The wildcard character to use.
|
|
* @param {string} singleChar The single character to use.
|
|
* @param {string} escapeChar The escape character to use.
|
|
* @param {boolean} matchCase Should we match case or not?
|
|
* @this {ol.Feature}
|
|
*/
|
|
ol.expr.lib[ol.expr.functions.LIKE] = function(attribute, value, wildCard,
|
|
singleChar, escapeChar, matchCase) {
|
|
if (wildCard == '.') {
|
|
throw new Error('"." is an unsupported wildCard character for ' +
|
|
'ol.filter.Comparison');
|
|
}
|
|
// set UMN MapServer defaults for unspecified parameters
|
|
wildCard = goog.isDef(wildCard) ? wildCard : '*';
|
|
singleChar = goog.isDef(singleChar) ? singleChar : '.';
|
|
escapeChar = goog.isDef(escapeChar) ? escapeChar : '!';
|
|
var val;
|
|
val = value.replace(
|
|
new RegExp('\\' + escapeChar + '(.|$)', 'g'), '\\$1');
|
|
val = value.replace(
|
|
new RegExp('\\' + singleChar, 'g'), '.');
|
|
val = value.replace(
|
|
new RegExp('\\' + wildCard, 'g'), '.*');
|
|
val = value.replace(
|
|
new RegExp('\\\\.\\*', 'g'), '\\' + wildCard);
|
|
val = value.replace(
|
|
new RegExp('\\\\\\.', 'g'), '\\' + singleChar);
|
|
var attributes = this.getAttributes();
|
|
var modifiers = (matchCase === false) ? 'gi' : 'g';
|
|
return new RegExp(val, modifiers).test(attributes[attribute]);
|
|
};
|
|
|
|
|
|
/**
|
|
* Case insensitive comparison for equality.
|
|
* @param {string} attribute Name of the attribute.
|
|
* @param {string} value Value to test for equality.
|
|
* @this {ol.Feature}
|
|
*/
|
|
ol.expr.lib[ol.expr.functions.IEQ] = function(attribute, value) {
|
|
var attributes = this.getAttributes();
|
|
if (goog.isString(value) && goog.isString(attributes[attribute])) {
|
|
return value.toUpperCase() == attributes[attribute].toUpperCase();
|
|
} else {
|
|
return value == attributes[attribute];
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Case insensitive comparison for non-equality.
|
|
* @param {string} attribute Name of the attribute.
|
|
* @param {string} value Value to test for non-equality.
|
|
* @this {ol.Feature}
|
|
*/
|
|
ol.expr.lib[ol.expr.functions.INEQ] = function(attribute, value) {
|
|
var attributes = this.getAttributes();
|
|
if (goog.isString(value) && goog.isString(attributes[attribute])) {
|
|
return value.toUpperCase() == attributes[attribute].toUpperCase();
|
|
} else {
|
|
return value != attributes[attribute];
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Determine if a feature's default geometry is of the given type.
|
|
* @param {ol.geom.GeometryType} type Geometry type.
|
|
* @return {boolean} The feature's default geometry is of the given type.
|
|
* @this {ol.Feature}
|
|
*/
|
|
ol.expr.lib[ol.expr.functions.GEOMETRY_TYPE] = function(type) {
|
|
var same = false;
|
|
var geometry = this.getGeometry();
|
|
if (geometry) {
|
|
same = geometry.getType() === type;
|
|
}
|
|
return same;
|
|
};
|
|
|
|
|
|
ol.expr.lib[ol.expr.functions.INTERSECTS] = function(geom, opt_projection,
|
|
opt_attribute) {
|
|
throw new Error('Spatial function not implemented: ' +
|
|
ol.expr.functions.INTERSECTS);
|
|
};
|
|
|
|
|
|
ol.expr.lib[ol.expr.functions.WITHIN] = function(geom, opt_projection,
|
|
opt_attribute) {
|
|
throw new Error('Spatial function not implemented: ' +
|
|
ol.expr.functions.WITHIN);
|
|
};
|
|
|
|
|
|
ol.expr.lib[ol.expr.functions.CONTAINS] = function(geom, opt_projeciton,
|
|
opt_attribute) {
|
|
throw new Error('Spatial function not implemented: ' +
|
|
ol.expr.functions.CONTAINS);
|
|
};
|
|
|
|
|
|
ol.expr.lib[ol.expr.functions.DWITHIN] = function(geom, distance, units,
|
|
opt_projection, opt_attribute) {
|
|
throw new Error('Spatial function not implemented: ' +
|
|
ol.expr.functions.DWITHIN);
|
|
};
|