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.} */ 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); };