Merge pull request #852 from bartvde/ogcfilter2

Add parser for OGC Filter 1.0 and 1.1 (read/write) (r=@tschaub,@ahocevar)
This commit is contained in:
Bart van den Eijnden
2013-08-15 02:12:58 -07:00
37 changed files with 2049 additions and 42 deletions

View File

@@ -97,7 +97,14 @@ ol.expr.lib = {};
ol.expr.functions = {
EXTENT: 'extent',
FID: 'fid',
GEOMETRY_TYPE: 'geometryType'
GEOMETRY_TYPE: 'geometryType',
INTERSECTS: 'intersects',
CONTAINS: 'contains',
DWITHIN: 'dwithin',
WITHIN: 'within',
LIKE: 'like',
IEQ: 'ieq',
INEQ: 'ineq'
};
@@ -107,12 +114,16 @@ ol.expr.functions = {
* @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) {
ol.expr.lib[ol.expr.functions.EXTENT] = function(minX, maxX, minY, maxY,
opt_projection, opt_attribute) {
var intersects = false;
var geometry = this.getGeometry();
var geometry = goog.isDef(opt_attribute) ?
this.get(opt_attribute) : this.getGeometry();
if (geometry) {
intersects = ol.extent.intersects(geometry.getBounds(),
[minX, maxX, minY, maxY]);
@@ -142,6 +153,72 @@ ol.expr.lib[ol.expr.functions.FID] = function(var_args) {
};
/**
* Determine if two strings are like one another, based on simple pattern
* matching.
* @param {string} value The string to test.
* @param {string} pattern The comparison pattern.
* @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(value, pattern, wildCard,
singleChar, escapeChar, matchCase) {
if (wildCard == '.') {
throw new Error('"." is an unsupported wildCard character for ' +
'the "like" function');
}
// set UMN MapServer defaults for unspecified parameters
wildCard = goog.isDef(wildCard) ? wildCard : '*';
singleChar = goog.isDef(singleChar) ? singleChar : '.';
escapeChar = goog.isDef(escapeChar) ? escapeChar : '!';
pattern = pattern.replace(
new RegExp('\\' + escapeChar + '(.|$)', 'g'), '\\$1');
pattern = pattern.replace(
new RegExp('\\' + singleChar, 'g'), '.');
pattern = pattern.replace(
new RegExp('\\' + wildCard, 'g'), '.*');
pattern = pattern.replace(
new RegExp('\\\\.\\*', 'g'), '\\' + wildCard);
pattern = pattern.replace(
new RegExp('\\\\\\.', 'g'), '\\' + singleChar);
var modifiers = (matchCase === false) ? 'gi' : 'g';
return new RegExp(pattern, modifiers).test(value);
};
/**
* Case insensitive comparison for equality.
* @param {*} first First value.
* @param {*} second Second value.
* @this {ol.Feature}
*/
ol.expr.lib[ol.expr.functions.IEQ] = function(first, second) {
if (goog.isString(first) && goog.isString(second)) {
return first.toUpperCase() == second.toUpperCase();
} else {
return first == second;
}
};
/**
* Case insensitive comparison for non-equality.
* @param {*} first First value.
* @param {*} second Second value.
* @this {ol.Feature}
*/
ol.expr.lib[ol.expr.functions.INEQ] = function(first, second) {
if (goog.isString(first) && goog.isString(second)) {
return first.toUpperCase() != second.toUpperCase();
} else {
return first != second;
}
};
/**
* Determine if a feature's default geometry is of the given type.
* @param {ol.geom.GeometryType} type Geometry type.
@@ -156,3 +233,31 @@ ol.expr.lib[ol.expr.functions.GEOMETRY_TYPE] = function(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);
};

View File

@@ -277,12 +277,12 @@ ol.expr.Identifier.prototype.getName = function() {
*
* @constructor
* @extends {ol.expr.Expression}
* @param {string|number|boolean|null} value A literal value.
* @param {string|number|boolean|Date|null} value A literal value.
*/
ol.expr.Literal = function(value) {
/**
* @type {string|number|boolean|null}
* @type {string|number|boolean|Date|null}
* @private
*/
this.value_ = value;
@@ -301,7 +301,7 @@ ol.expr.Literal.prototype.evaluate = function() {
/**
* Get the literal value.
* @return {string|number|boolean|null} The literal value.
* @return {string|number|boolean|Date|null} The literal value.
*/
ol.expr.Literal.prototype.getValue = function() {
return this.value_;

View File

@@ -0,0 +1,37 @@
goog.provide('ol.parser.ogc.Filter');
goog.require('ol.parser.ogc.Filter_v1_0_0');
goog.require('ol.parser.ogc.Filter_v1_1_0');
goog.require('ol.parser.ogc.Versioned');
/**
* @define {boolean} Whether to enable OGC Filter version 1.0.0.
*/
ol.ENABLE_OGCFILTER_1_0_0 = true;
/**
* @define {boolean} Whether to enable OGC Filter version 1.1.0.
*/
ol.ENABLE_OGCFILTER_1_1_0 = true;
/**
* @constructor
* @param {Object=} opt_options Options which will be set on this object.
* @extends {ol.parser.ogc.Versioned}
*/
ol.parser.ogc.Filter = function(opt_options) {
opt_options = opt_options || {};
opt_options['defaultVersion'] = '1.0.0';
this.parsers = {};
if (ol.ENABLE_OGCFILTER_1_0_0) {
this.parsers['v1_0_0'] = ol.parser.ogc.Filter_v1_0_0;
}
if (ol.ENABLE_OGCFILTER_1_1_0) {
this.parsers['v1_1_0'] = ol.parser.ogc.Filter_v1_1_0;
}
goog.base(this, opt_options);
};
goog.inherits(ol.parser.ogc.Filter, ol.parser.ogc.Versioned);

View File

@@ -0,0 +1,607 @@
goog.provide('ol.parser.ogc.Filter_v1');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.dom.xml');
goog.require('goog.object');
goog.require('goog.string');
goog.require('ol.expr');
goog.require('ol.expr.Call');
goog.require('ol.expr.Comparison');
goog.require('ol.expr.ComparisonOp');
goog.require('ol.expr.Identifier');
goog.require('ol.expr.Literal');
goog.require('ol.expr.Logical');
goog.require('ol.expr.LogicalOp');
goog.require('ol.expr.Math');
goog.require('ol.expr.MathOp');
goog.require('ol.expr.Not');
goog.require('ol.expr.functions');
goog.require('ol.parser.XML');
/**
* @constructor
* @extends {ol.parser.XML}
*/
ol.parser.ogc.Filter_v1 = function() {
this.defaultNamespaceURI = 'http://www.opengis.net/ogc';
this.errorProperty = 'filter';
this.readers = {
'http://www.opengis.net/ogc': {
_expression: function(node) {
var expressions = [];
var obj, value, numValue, expr;
for (var child = node.firstChild; child; child = child.nextSibling) {
switch (child.nodeType) {
case 1:
obj = this.readNode(child);
if (obj.property) {
expressions.push(obj.property);
} else if (goog.isDef(obj.value)) {
return obj.value;
}
break;
case 3: // text node
case 4: // cdata section
value = goog.string.trim(child.nodeValue);
// no need to concatenate empty strings
if (value) {
// check for numeric values
numValue = goog.string.toNumber(value);
if (!isNaN(numValue)) {
value = numValue;
}
expressions.push(new ol.expr.Literal(value));
}
break;
default:
break;
}
}
// if we have more than one property or literal, we concatenate them
var num = expressions.length;
if (num === 1) {
expr = expressions[0];
} else {
expr = new ol.expr.Literal('');
if (num > 1) {
var add = ol.expr.MathOp.ADD;
for (var i = 0; i < num; ++i) {
expr = new ol.expr.Math(add, expr, expressions[i]);
}
}
}
return expr;
},
'Filter': function(node, obj) {
var container = {
filters: []
};
this.readChildNodes(node, container);
if (goog.isDef(container.fids)) {
obj.filter = new ol.expr.Call(
new ol.expr.Identifier(ol.expr.functions.FID),
goog.object.getValues(container.fids));
} else if (container.filters.length > 0) {
obj.filter = container.filters[0];
}
},
'FeatureId': function(node, obj) {
var fid = node.getAttribute('fid');
if (fid) {
if (!goog.isDef(obj.fids)) {
obj.fids = {};
}
if (!obj.fids.hasOwnProperty(fid)) {
obj.fids[fid] = new ol.expr.Literal(fid);
}
}
},
'And': function(node, obj) {
var container = {filters: []};
this.readChildNodes(node, container);
var filter = this.aggregateLogical_(container.filters,
ol.expr.LogicalOp.AND);
obj.filters.push(filter);
},
'Or': function(node, obj) {
var container = {filters: []};
this.readChildNodes(node, container);
var filter = this.aggregateLogical_(container.filters,
ol.expr.LogicalOp.OR);
obj.filters.push(filter);
},
'Not': function(node, obj) {
var container = {filters: []};
this.readChildNodes(node, container);
// Not is unary so can only contain 1 child filter
obj.filters.push(new ol.expr.Not(
container.filters[0]));
},
'PropertyIsNull': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.EQ,
container.property,
new ol.expr.Literal(null)));
},
'PropertyIsLessThan': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.LT,
container.property,
container.value));
},
'PropertyIsGreaterThan': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.GT,
container.property,
container.value));
},
'PropertyIsLessThanOrEqualTo': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.LTE,
container.property,
container.value));
},
'PropertyIsGreaterThanOrEqualTo': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.GTE,
container.property,
container.value));
},
'PropertyIsBetween': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Logical(ol.expr.LogicalOp.AND,
new ol.expr.Comparison(ol.expr.ComparisonOp.GTE,
container.property, container.lowerBoundary),
new ol.expr.Comparison(ol.expr.ComparisonOp.LTE,
container.property, container.upperBoundary)));
},
'Literal': function(node, obj) {
var nodeValue = this.getChildValue(node);
var value = goog.string.toNumber(nodeValue);
obj.value = new ol.expr.Literal(isNaN(value) ? nodeValue : value);
},
'PropertyName': function(node, obj) {
obj.property = new ol.expr.Identifier(this.getChildValue(node));
},
'LowerBoundary': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
obj.lowerBoundary = readers._expression.call(this, node);
},
'UpperBoundary': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
obj.upperBoundary = readers._expression.call(this, node);
},
_spatial: function(node, obj, identifier) {
var args = [], container = {};
this.readChildNodes(node, container);
if (goog.isDef(container.geometry)) {
args.push(new ol.expr.Literal(this.gml_.createGeometry(container)));
} else {
args = [new ol.expr.Literal(container.bounds[0]),
new ol.expr.Literal(container.bounds[1]),
new ol.expr.Literal(container.bounds[2]),
new ol.expr.Literal(container.bounds[3])];
}
if (goog.isDef(container.distance)) {
args.push(container.distance);
}
if (goog.isDef(container.distanceUnits)) {
args.push(container.distanceUnits);
}
args.push(new ol.expr.Literal(container.projection));
if (goog.isDef(container.property)) {
args.push(container.property);
}
obj.filters.push(new ol.expr.Call(new ol.expr.Identifier(
identifier), args));
},
'BBOX': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
readers._spatial.call(this, node, obj,
ol.expr.functions.EXTENT);
},
'Intersects': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
readers._spatial.call(this, node, obj,
ol.expr.functions.INTERSECTS);
},
'Within': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
readers._spatial.call(this, node, obj,
ol.expr.functions.WITHIN);
},
'Contains': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
readers._spatial.call(this, node, obj,
ol.expr.functions.CONTAINS);
},
'DWithin': function(node, obj) {
var readers = this.readers[this.defaultNamespaceURI];
readers._spatial.call(this, node, obj,
ol.expr.functions.DWITHIN);
},
'Distance': function(node, obj) {
var value = goog.string.toNumber(this.getChildValue(node));
obj.distance = new ol.expr.Literal(value);
obj.distanceUnits = new ol.expr.Literal(node.getAttribute('units'));
}
}
};
this.writers = {
'http://www.opengis.net/ogc': {
'Filter': function(filter) {
var node = this.createElementNS('ogc:Filter');
this.writeNode(this.getFilterType_(filter), filter, null, node);
return node;
},
'_featureIds': function(filter) {
var node = this.createDocumentFragment();
var args = filter.getArgs();
for (var i = 0, ii = args.length; i < ii; i++) {
goog.asserts.assert(args[i] instanceof ol.expr.Literal);
this.writeNode('FeatureId', args[i].getValue(), null, node);
}
return node;
},
'FeatureId': function(fid) {
var node = this.createElementNS('ogc:FeatureId');
node.setAttribute('fid', fid);
return node;
},
'And': function(filter) {
var node = this.createElementNS('ogc:And');
var subFilters = [];
this.getSubfiltersForLogical_(filter, subFilters);
for (var i = 0, ii = subFilters.length; i < ii; ++i) {
var subFilter = subFilters[i];
if (goog.isDefAndNotNull(subFilter)) {
this.writeNode(this.getFilterType_(subFilter), subFilter,
null, node);
}
}
return node;
},
'Or': function(filter) {
var node = this.createElementNS('ogc:Or');
var subFilters = [];
this.getSubfiltersForLogical_(filter, subFilters);
for (var i = 0, ii = subFilters.length; i < ii; ++i) {
var subFilter = subFilters[i];
if (goog.isDefAndNotNull(subFilter)) {
this.writeNode(this.getFilterType_(subFilter), subFilter,
null, node);
}
}
return node;
},
'Not': function(filter) {
var node = this.createElementNS('ogc:Not');
var childFilter = filter.getArgument();
this.writeNode(this.getFilterType_(childFilter), childFilter, null,
node);
return node;
},
'PropertyIsLessThan': function(filter) {
var node = this.createElementNS('ogc:PropertyIsLessThan');
this.writeNode('PropertyName', filter.getLeft(), null, node);
this.writeOgcExpression(filter.getRight(), node);
return node;
},
'PropertyIsGreaterThan': function(filter) {
var node = this.createElementNS('ogc:PropertyIsGreaterThan');
this.writeNode('PropertyName', filter.getLeft(), null, node);
this.writeOgcExpression(filter.getRight(), node);
return node;
},
'PropertyIsLessThanOrEqualTo': function(filter) {
var node = this.createElementNS('ogc:PropertyIsLessThanOrEqualTo');
this.writeNode('PropertyName', filter.getLeft(), null, node);
this.writeOgcExpression(filter.getRight(), node);
return node;
},
'PropertyIsGreaterThanOrEqualTo': function(filter) {
var node = this.createElementNS('ogc:PropertyIsGreaterThanOrEqualTo');
this.writeNode('PropertyName', filter.getLeft(), null, node);
this.writeOgcExpression(filter.getRight(), node);
return node;
},
'PropertyIsBetween': function(filter) {
var node = this.createElementNS('ogc:PropertyIsBetween');
var property = filter.getLeft().getLeft();
this.writeNode('PropertyName', property, null, node);
var lower, upper;
var filters = new Array(2);
filters[0] = filter.getLeft();
filters[1] = filter.getRight();
for (var i = 0; i < 2; ++i) {
var expr = filters[i].getRight();
if (filters[i].getOperator() === ol.expr.ComparisonOp.GTE) {
lower = expr;
} else if (filters[i].getOperator() === ol.expr.ComparisonOp.LTE) {
upper = expr;
}
}
this.writeNode('LowerBoundary', lower, null, node);
this.writeNode('UpperBoundary', upper, null, node);
return node;
},
'PropertyName': function(expr) {
goog.asserts.assert(expr instanceof ol.expr.Identifier);
var node = this.createElementNS('ogc:PropertyName');
node.appendChild(this.createTextNode(expr.getName()));
return node;
},
'Literal': function(expr) {
goog.asserts.assert(expr instanceof ol.expr.Literal);
var node = this.createElementNS('ogc:Literal');
node.appendChild(this.createTextNode(expr.getValue()));
return node;
},
'LowerBoundary': function(expr) {
var node = this.createElementNS('ogc:LowerBoundary');
this.writeOgcExpression(expr, node);
return node;
},
'UpperBoundary': function(expr) {
var node = this.createElementNS('ogc:UpperBoundary');
this.writeOgcExpression(expr, node);
return node;
},
'INTERSECTS': function(filter) {
return this.writeSpatial_(filter, 'Intersects');
},
'WITHIN': function(filter) {
return this.writeSpatial_(filter, 'Within');
},
'CONTAINS': function(filter) {
return this.writeSpatial_(filter, 'Contains');
},
'DWITHIN': function(filter) {
var node = this.writeSpatial_(filter, 'DWithin');
this.writeNode('Distance', filter, null, node);
return node;
},
'Distance': function(filter) {
var node = this.createElementNS('ogc:Distance');
var args = filter.getArgs();
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
node.setAttribute('units', args[2].getValue());
goog.asserts.assert(args[1] instanceof ol.expr.Literal);
node.appendChild(this.createTextNode(args[1].getValue()));
return node;
},
'Function': function(filter) {
var node = this.createElementNS('ogc:Function');
node.setAttribute('name', filter.getCallee().getName());
var params = filter.getArgs();
for (var i = 0, len = params.length; i < len; i++) {
this.writeOgcExpression(params[i], node);
}
return node;
},
'PropertyIsNull': function(filter) {
var node = this.createElementNS('ogc:PropertyIsNull');
this.writeNode('PropertyName', filter.getLeft(), null, node);
return node;
}
}
};
goog.base(this);
};
goog.inherits(ol.parser.ogc.Filter_v1, ol.parser.XML);
/**
* @private
*/
ol.parser.ogc.Filter_v1.filterMap_ = {
'&&': 'And',
'||': 'Or',
'!': 'Not',
'==': 'PropertyIsEqualTo',
'!=': 'PropertyIsNotEqualTo',
'<': 'PropertyIsLessThan',
'>': 'PropertyIsGreaterThan',
'<=': 'PropertyIsLessThanOrEqualTo',
'>=': 'PropertyIsGreaterThanOrEqualTo',
'..': 'PropertyIsBetween',
'like': 'PropertyIsLike',
'null': 'PropertyIsNull',
'extent': 'BBOX',
'dwithin': 'DWITHIN',
'within': 'WITHIN',
'contains': 'CONTAINS',
'intersects': 'INTERSECTS',
'fid': '_featureIds',
'ieq': 'PropertyIsEqualTo',
'ineq': 'PropertyIsNotEqualTo'
};
/**
* @param {ol.expr.Expression} filter The filter to determine the type of.
* @return {string} The type of filter.
* @private
*/
ol.parser.ogc.Filter_v1.prototype.getFilterType_ = function(filter) {
var type;
if (filter instanceof ol.expr.Logical ||
filter instanceof ol.expr.Comparison) {
type = filter.getOperator();
var left = filter.getLeft();
var right = filter.getRight();
var isNull = (type === ol.expr.ComparisonOp.EQ &&
right instanceof ol.expr.Literal && right.getValue() === null);
if (isNull) {
type = 'null';
}
var isBetween = (type === ol.expr.LogicalOp.AND &&
left instanceof ol.expr.Comparison &&
right instanceof ol.expr.Comparison &&
left.getLeft() instanceof ol.expr.Identifier &&
right.getLeft() instanceof ol.expr.Identifier &&
left.getLeft().getName() === right.getLeft().getName() &&
(left.getOperator() === ol.expr.ComparisonOp.LTE ||
left.getOperator() === ol.expr.ComparisonOp.GTE) &&
(right.getOperator() === ol.expr.ComparisonOp.LTE ||
right.getOperator() === ol.expr.ComparisonOp.GTE));
if (isBetween) {
type = '..';
}
} else if (filter instanceof ol.expr.Call) {
var callee = filter.getCallee();
goog.asserts.assert(callee instanceof ol.expr.Identifier);
type = callee.getName();
} else if (filter instanceof ol.expr.Not) {
type = '!';
}
var filterType = ol.parser.ogc.Filter_v1.filterMap_[type];
if (!filterType) {
throw new Error('Filter writing not supported for rule type: ' + type);
}
return filterType;
};
/**
* @param {string|Document|Element} data Data to read.
* @return {Object} An object representing the document.
*/
ol.parser.ogc.Filter_v1.prototype.read = function(data) {
if (goog.isString(data)) {
data = goog.dom.xml.loadXml(data);
}
if (data && data.nodeType == 9) {
data = data.documentElement;
}
var obj = {};
this.readNode(data, obj);
return obj.filter;
};
/**
* @param {ol.expr.Expression} filter The filter to write out.
* @return {string} The serialized filter.
*/
ol.parser.ogc.Filter_v1.prototype.write = function(filter) {
var root = this.writeNode('Filter', filter);
this.setAttributeNS(
root, 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation', this.schemaLocation);
return this.serialize(root);
};
/**
* @param {ol.expr.Expression} expr The value write out.
* @param {Element} node The node to append to.
* @return {Element} The node to which was appended.
* @protected
*/
ol.parser.ogc.Filter_v1.prototype.writeOgcExpression = function(expr, node) {
if (expr instanceof ol.expr.Call) {
this.writeNode('Function', expr, null, node);
} else if (expr instanceof ol.expr.Literal) {
this.writeNode('Literal', expr, null, node);
} else if (expr instanceof ol.expr.Identifier) {
this.writeNode('PropertyName', expr, null, node);
}
return node;
};
/**
* @param {ol.expr.Logical} filter The filter to retrieve the sub filters from.
* @param {Array.<ol.expr.Expression>} subFilters The sub filters retrieved.
* @private
*/
ol.parser.ogc.Filter_v1.prototype.getSubfiltersForLogical_ = function(filter,
subFilters) {
var operator = filter.getOperator();
var filters = new Array(2);
filters[0] = filter.getLeft();
filters[1] = filter.getRight();
for (var i = 0; i < 2; ++i) {
if (filters[i] instanceof ol.expr.Logical && filters[i].getOperator() ===
operator) {
this.getSubfiltersForLogical_(filters[i], subFilters);
} else {
subFilters.push(filters[i]);
}
}
};
/**
* Since ol.expr.Logical can only have a left and a right, we need to nest
* sub filters coming from OGC Filter.
* @param {Array.<ol.expr.Expression>} filters The filters to aggregate.
* @param {ol.expr.LogicalOp} operator The logical operator to use.
* @return {ol.expr.Logical} The logical filter created.
* @private
*/
ol.parser.ogc.Filter_v1.prototype.aggregateLogical_ = function(filters,
operator) {
var subFilters = [];
var newFilters = [];
// we only need to do this if we have more than 2 items
if (filters.length > 2) {
while (filters.length) {
subFilters.push(filters.pop());
if (subFilters.length === 2) {
newFilters.push(new ol.expr.Logical(operator,
subFilters[0], subFilters[1]));
subFilters.length = 0;
}
}
// there could be a single item left now
if (subFilters.length === 1) {
newFilters.push(subFilters[0]);
}
return this.aggregateLogical_(newFilters, operator);
} else {
return new ol.expr.Logical(operator, filters[0], filters[1]);
}
};
/**
* @param {ol.parser.ogc.GML_v2|ol.parser.ogc.GML_v3} gml The GML parser to
* use.
* @protected
*/
ol.parser.ogc.Filter_v1.prototype.setGmlParser = function(gml) {
this.gml_ = gml;
for (var uri in this.gml_.readers) {
for (var key in this.gml_.readers[uri]) {
if (!goog.isDef(this.readers[uri])) {
this.readers[uri] = {};
}
this.readers[uri][key] = goog.bind(this.gml_.readers[uri][key],
this.gml_);
}
}
for (uri in this.gml_.writers) {
for (key in this.gml_.writers[uri]) {
if (!goog.isDef(this.writers[uri])) {
this.writers[uri] = {};
}
this.writers[uri][key] = goog.bind(this.gml_.writers[uri][key],
this.gml_);
}
}
};

View File

@@ -0,0 +1,179 @@
goog.provide('ol.parser.ogc.Filter_v1_0_0');
goog.require('goog.asserts');
goog.require('goog.object');
goog.require('ol.expr');
goog.require('ol.expr.Call');
goog.require('ol.expr.Comparison');
goog.require('ol.expr.ComparisonOp');
goog.require('ol.expr.Identifier');
goog.require('ol.expr.Literal');
goog.require('ol.expr.functions');
goog.require('ol.geom.Geometry');
goog.require('ol.parser.ogc.Filter_v1');
goog.require('ol.parser.ogc.GML_v2');
/**
* @constructor
* @extends {ol.parser.ogc.Filter_v1}
*/
ol.parser.ogc.Filter_v1_0_0 = function() {
goog.base(this);
this.version = '1.0.0';
this.schemaLocation = 'http://www.opengis.net/ogc ' +
'http://schemas.opengis.net/filter/1.0.0/filter.xsd';
goog.object.extend(this.readers['http://www.opengis.net/ogc'], {
'PropertyIsEqualTo': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.EQ,
container.property,
container.value));
},
'PropertyIsNotEqualTo': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
obj.filters.push(new ol.expr.Comparison(
ol.expr.ComparisonOp.NEQ,
container.property,
container.value));
},
'PropertyIsLike': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
var args = [];
args.push(container.property, container.value,
new ol.expr.Literal(node.getAttribute('wildCard')),
new ol.expr.Literal(node.getAttribute('singleChar')),
new ol.expr.Literal(node.getAttribute('escape')));
obj.filters.push(new ol.expr.Call(
new ol.expr.Identifier(ol.expr.functions.LIKE), args));
}
});
goog.object.extend(this.writers['http://www.opengis.net/ogc'], {
'PropertyIsEqualTo': function(filter) {
var node = this.createElementNS('ogc:PropertyIsEqualTo');
var property = filter.getLeft();
this.writeNode('PropertyName', property, null, node);
this.writeOgcExpression(filter.getRight(), node);
return node;
},
'PropertyIsNotEqualTo': function(filter) {
var node = this.createElementNS('ogc:PropertyIsNotEqualTo');
var property = filter.getLeft();
this.writeNode('PropertyName', property, null, node);
this.writeOgcExpression(filter.getRight(), node);
return node;
},
'PropertyIsLike': function(filter) {
var node = this.createElementNS('ogc:PropertyIsLike');
var args = filter.getArgs();
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
node.setAttribute('wildCard', args[2].getValue());
goog.asserts.assert(args[3] instanceof ol.expr.Literal);
node.setAttribute('singleChar', args[3].getValue());
goog.asserts.assert(args[4] instanceof ol.expr.Literal);
node.setAttribute('escape', args[4].getValue());
var property = args[0];
if (goog.isDef(property)) {
this.writeNode('PropertyName', property, null, node);
}
this.writeNode('Literal', args[1], null, node);
return node;
},
'BBOX': function(filter) {
var node = this.createElementNS('ogc:BBOX');
var args = filter.getArgs();
goog.asserts.assert(args[0] instanceof ol.expr.Literal);
goog.asserts.assert(args[1] instanceof ol.expr.Literal);
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
goog.asserts.assert(args[3] instanceof ol.expr.Literal);
goog.asserts.assert(args[4] instanceof ol.expr.Literal);
var property = args[5], bbox = [args[0].getValue(), args[1].getValue(),
args[2].getValue(), args[3].getValue()],
projection = args[4].getValue();
// PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
// accepts filters without it.
if (goog.isDefAndNotNull(property)) {
this.writeNode('PropertyName', property, null, node);
}
var box = this.writeNode('Box', bbox,
'http://www.opengis.net/gml');
if (goog.isDefAndNotNull(projection)) {
box.setAttribute('srsName', projection);
}
node.appendChild(box);
return node;
}
});
this.setGmlParser(new ol.parser.ogc.GML_v2({featureNS: 'http://foo'}));
};
goog.inherits(ol.parser.ogc.Filter_v1_0_0,
ol.parser.ogc.Filter_v1);
/**
* @param {ol.expr.Call} filter The filter to write out.
* @param {string} name The name of the spatial operator.
* @return {Element} The node created.
* @private
*/
ol.parser.ogc.Filter_v1_0_0.prototype.writeSpatial_ = function(filter, name) {
var node = this.createElementNS('ogc:' + name);
var args = filter.getArgs();
var property, geom = null, bbox, call, projection;
if (args[0] instanceof ol.expr.Literal && goog.isNumber(args[0].getValue())) {
goog.asserts.assert(args[1] instanceof ol.expr.Literal);
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
goog.asserts.assert(args[3] instanceof ol.expr.Literal);
bbox = [args[0].getValue(), args[1].getValue(), args[2].getValue(),
args[3].getValue()];
projection = args[4];
property = args[5];
} else if (args[0] instanceof ol.expr.Literal &&
args[0].getValue() instanceof ol.geom.Geometry) {
geom = args[0].getValue();
if (name === 'DWithin') {
projection = args[3];
property = args[4];
} else {
projection = args[1];
property = args[2];
}
} else if (args[0] instanceof ol.expr.Call) {
call = args[0];
if (name === 'DWithin') {
projection = args[3];
property = args[4];
} else {
projection = args[1];
property = args[2];
}
}
if (goog.isDefAndNotNull(property)) {
this.writeNode('PropertyName', property, null, node);
}
if (goog.isDef(call)) {
this.writeNode('Function', call, null, node);
} else {
var child;
if (geom !== null) {
child = this.writeNode('_geometry', geom,
this.gml_.featureNS).firstChild;
} else if (bbox.length === 4) {
child = this.writeNode('Box', bbox,
'http://www.opengis.net/gml');
}
if (goog.isDef(child)) {
goog.asserts.assert(projection instanceof ol.expr.Literal);
if (goog.isDefAndNotNull(projection.getValue())) {
child.setAttribute('srsName', projection.getValue());
}
node.appendChild(child);
}
}
return node;
};

View File

@@ -0,0 +1,237 @@
goog.provide('ol.parser.ogc.Filter_v1_1_0');
goog.require('goog.asserts');
goog.require('goog.object');
goog.require('ol.expr');
goog.require('ol.expr.Call');
goog.require('ol.expr.Comparison');
goog.require('ol.expr.ComparisonOp');
goog.require('ol.expr.Identifier');
goog.require('ol.expr.Literal');
goog.require('ol.expr.functions');
goog.require('ol.geom.Geometry');
goog.require('ol.parser.ogc.Filter_v1');
goog.require('ol.parser.ogc.GML_v3');
/**
* @constructor
* @extends {ol.parser.ogc.Filter_v1}
*/
ol.parser.ogc.Filter_v1_1_0 = function() {
goog.base(this);
this.version = '1.1.0';
this.schemaLocation = 'http://www.opengis.net/ogc ' +
'http://schemas.opengis.net/filter/1.1.0/filter.xsd';
goog.object.extend(this.readers['http://www.opengis.net/ogc'], {
'PropertyIsEqualTo': function(node, obj) {
var matchCase = node.getAttribute('matchCase');
var container = {}, filter;
this.readChildNodes(node, container);
if (matchCase === 'false' || matchCase === '0') {
filter = new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.IEQ),
[container.property, container.value]);
} else {
filter = new ol.expr.Comparison(
ol.expr.ComparisonOp.EQ,
container.property,
container.value);
}
obj.filters.push(filter);
},
'PropertyIsNotEqualTo': function(node, obj) {
var matchCase = node.getAttribute('matchCase');
var container = {}, filter;
this.readChildNodes(node, container);
if (matchCase === 'false' || matchCase === '0') {
filter = new ol.expr.Call(new ol.expr.Identifier(
ol.expr.functions.INEQ),
[container.property, container.value]);
} else {
filter = new ol.expr.Comparison(
ol.expr.ComparisonOp.NEQ,
container.property,
container.value);
}
obj.filters.push(filter);
},
'PropertyIsLike': function(node, obj) {
var container = {};
this.readChildNodes(node, container);
var args = [];
args.push(container.property, container.value,
new ol.expr.Literal(node.getAttribute('wildCard')),
new ol.expr.Literal(node.getAttribute('singleChar')),
new ol.expr.Literal(node.getAttribute('escapeChar')),
new ol.expr.Literal(node.getAttribute('matchCase')));
obj.filters.push(new ol.expr.Call(
new ol.expr.Identifier(ol.expr.functions.LIKE), args));
}
});
goog.object.extend(this.writers['http://www.opengis.net/ogc'], {
'PropertyIsEqualTo': function(filter) {
var node = this.createElementNS('ogc:PropertyIsEqualTo');
var property, value;
if (filter instanceof ol.expr.Call) {
var args = filter.getArgs();
property = args[0];
value = args[1];
node.setAttribute('matchCase', false);
} else {
property = filter.getLeft();
value = filter.getRight();
}
this.writeNode('PropertyName', property, null, node);
this.writeOgcExpression(value, node);
return node;
},
'PropertyIsNotEqualTo': function(filter) {
var node = this.createElementNS('ogc:PropertyIsNotEqualTo');
var property, value;
if (filter instanceof ol.expr.Call) {
var args = filter.getArgs();
property = args[0];
value = args[1];
node.setAttribute('matchCase', false);
} else {
property = filter.getLeft();
value = filter.getRight();
}
this.writeNode('PropertyName', property, null, node);
this.writeOgcExpression(value, node);
return node;
},
'PropertyIsLike': function(filter) {
var node = this.createElementNS('ogc:PropertyIsLike');
var args = filter.getArgs();
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
goog.asserts.assert(args[3] instanceof ol.expr.Literal);
goog.asserts.assert(args[4] instanceof ol.expr.Literal);
node.setAttribute('wildCard', args[2].getValue());
node.setAttribute('singleChar', args[3].getValue());
node.setAttribute('escapeChar', args[4].getValue());
if (goog.isDefAndNotNull(args[5])) {
goog.asserts.assert(args[5] instanceof ol.expr.Literal);
node.setAttribute('matchCase', args[5].getValue());
}
var property = args[0];
if (goog.isDef(property)) {
this.writeNode('PropertyName', property, null, node);
}
this.writeNode('Literal', args[1], null, node);
return node;
},
'BBOX': function(filter) {
var node = this.createElementNS('ogc:BBOX');
var args = filter.getArgs();
goog.asserts.assert(args[0] instanceof ol.expr.Literal);
goog.asserts.assert(args[1] instanceof ol.expr.Literal);
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
goog.asserts.assert(args[3] instanceof ol.expr.Literal);
goog.asserts.assert(args[4] instanceof ol.expr.Literal);
var property = args[5], bbox = [args[0].getValue(), args[1].getValue(),
args[2].getValue(), args[3].getValue()],
projection = args[4].getValue();
// PropertyName is optional in 1.1.0
if (goog.isDefAndNotNull(property)) {
this.writeNode('PropertyName', property, null, node);
}
var box = this.writeNode('Envelope', bbox,
'http://www.opengis.net/gml');
if (goog.isDefAndNotNull(projection)) {
box.setAttribute('srsName', projection);
}
node.appendChild(box);
return node;
},
'SortBy': function(sortProperties) {
var node = this.createElementNS('ogc:SortBy');
for (var i = 0, l = sortProperties.length; i < l; i++) {
this.writeNode('SortProperty', sortProperties[i], null, node);
}
return node;
},
'SortProperty': function(sortProperty) {
var node = this.createElementNS('ogc:SortProperty');
this.writeNode('PropertyName', sortProperty['property'], null, node);
goog.asserts.assert(sortProperty['order'] instanceof ol.expr.Literal);
this.writeNode('SortOrder',
(sortProperty['order'].getValue() == 'DESC') ? 'DESC' : 'ASC', null,
node);
return node;
},
'SortOrder': function(value) {
var node = this.createElementNS('ogc:SortOrder');
node.appendChild(this.createTextNode(value));
return node;
}
});
this.setGmlParser(new ol.parser.ogc.GML_v3());
};
goog.inherits(ol.parser.ogc.Filter_v1_1_0,
ol.parser.ogc.Filter_v1);
/**
* @param {ol.expr.Call} filter The filter to write out.
* @param {string} name The name of the spatial operator.
* @return {Element} The node created.
* @private
*/
ol.parser.ogc.Filter_v1_1_0.prototype.writeSpatial_ = function(filter, name) {
var node = this.createElementNS('ogc:' + name);
var args = filter.getArgs();
var property, geom = null, bbox, call, projection;
if (args[0] instanceof ol.expr.Literal && goog.isNumber(args[0].getValue())) {
goog.asserts.assert(args[1] instanceof ol.expr.Literal);
goog.asserts.assert(args[2] instanceof ol.expr.Literal);
goog.asserts.assert(args[3] instanceof ol.expr.Literal);
bbox = [args[0].getValue(), args[1].getValue(), args[2].getValue(),
args[3].getValue()];
projection = args[4];
property = args[5];
} else if (args[0] instanceof ol.expr.Literal &&
args[0].getValue() instanceof ol.geom.Geometry) {
geom = args[0].getValue();
if (name === 'DWithin') {
projection = args[3];
property = args[4];
} else {
projection = args[1];
property = args[2];
}
} else if (args[0] instanceof ol.expr.Call) {
call = args[0];
if (name === 'DWithin') {
projection = args[3];
property = args[4];
} else {
projection = args[1];
property = args[2];
}
}
if (goog.isDefAndNotNull(property)) {
this.writeNode('PropertyName', property, null, node);
}
if (goog.isDef(call)) {
this.writeNode('Function', call, null, node);
} else {
var child;
if (geom !== null) {
child = this.writeNode('_geometry', geom,
this.gml_.featureNS).firstChild;
} else if (bbox.length === 4) {
child = this.writeNode('Envelope', bbox,
'http://www.opengis.net/gml');
}
if (goog.isDef(child)) {
goog.asserts.assert(projection instanceof ol.expr.Literal);
if (goog.isDefAndNotNull(projection.getValue())) {
child.setAttribute('srsName', projection.getValue());
}
node.appendChild(child);
}
}
return node;
};

View File

@@ -310,7 +310,7 @@ ol.parser.ogc.GML = function(opt_options) {
sharedVertices = callback(feature, geom.type);
}
}
var geometry = this.createGeometry_({geometry: geom},
var geometry = this.createGeometry({geometry: geom},
sharedVertices);
if (goog.isDef(geometry)) {
feature.setGeometry(geometry);
@@ -460,7 +460,7 @@ ol.parser.ogc.GML = function(opt_options) {
} else if (type === ol.geom.GeometryType.GEOMETRYCOLLECTION) {
child = this.writeNode('GeometryCollection', geometry, null, node);
}
if (goog.isDef(this.srsName)) {
if (goog.isDefAndNotNull(this.srsName)) {
this.setAttributeNS(child, null, 'srsName', this.srsName);
}
return node;
@@ -550,13 +550,12 @@ ol.parser.ogc.GML.prototype.readNode = function(node, obj, opt_first) {
/**
* @private
* @param {Object} container Geometry container.
* @param {ol.geom.SharedVertices=} opt_vertices Shared vertices.
* @return {ol.geom.Geometry} The geometry created.
*/
// TODO use a mixin since this is also used in the KML parser
ol.parser.ogc.GML.prototype.createGeometry_ = function(container,
ol.parser.ogc.GML.prototype.createGeometry = function(container,
opt_vertices) {
var geometry = null, coordinates, i, ii;
switch (container.geometry.type) {
@@ -600,7 +599,7 @@ ol.parser.ogc.GML.prototype.createGeometry_ = function(container,
case ol.geom.GeometryType.GEOMETRYCOLLECTION:
var geometries = [];
for (i = 0, ii = container.geometry.parts.length; i < ii; i++) {
geometries.push(this.createGeometry_({
geometries.push(this.createGeometry({
geometry: container.geometry.parts[i]
}, opt_vertices));
}

View File

@@ -31,6 +31,7 @@ ol.parser.ogc.GML_v2 = function(opt_options) {
this.readers[this.defaultNamespaceURI]['_inherit'].apply(this,
[node, coordinates, container]);
this.readChildNodes(node, coordinates);
container.projection = node.getAttribute('srsName');
container.bounds = [coordinates[0][0][0], coordinates[0][1][0],
coordinates[0][0][1], coordinates[0][1][1]];
}
@@ -100,10 +101,10 @@ ol.parser.ogc.GML_v2 = function(opt_options) {
},
'Box': function(extent) {
var node = this.createElementNS('gml:Box');
this.writeNode('coordinates', [[extent.minX, extent.minY],
[extent.maxX, extent.maxY]], null, node);
this.writeNode('coordinates', [[extent[0], extent[1]],
[extent[2], extent[3]]], null, node);
// srsName attribute is optional for gml:Box
if (goog.isDef(this.srsName)) {
if (goog.isDefAndNotNull(this.srsName)) {
node.setAttribute('srsName', this.srsName);
}
return node;

View File

@@ -56,7 +56,7 @@ ol.parser.ogc.GML_v3 = function(opt_options) {
} else if (type === ol.geom.GeometryType.GEOMETRYCOLLECTION) {
child = this.writeNode('MultiGeometry', geometry, null, node);
}
if (goog.isDef(this.srsName)) {
if (goog.isDefAndNotNull(this.srsName)) {
this.setAttributeNS(child, null, 'srsName', this.srsName);
}
return node;
@@ -211,6 +211,7 @@ ol.parser.ogc.GML_v3 = function(opt_options) {
this.readers[this.defaultNamespaceURI]['_inherit'].apply(this,
[node, coordinates, container]);
this.readChildNodes(node, coordinates);
container.projection = node.getAttribute('srsName');
container.bounds = [coordinates[0][0][0][0], coordinates[1][0][0][0],
coordinates[0][0][0][1], coordinates[1][0][0][1]];
},
@@ -387,9 +388,9 @@ ol.parser.ogc.GML_v3 = function(opt_options) {
// only 2d for simple features profile
var pos;
if (this.axisOrientation.substr(0, 2) === 'en') {
pos = (bounds.left + ' ' + bounds.bottom);
pos = (bounds[0] + ' ' + bounds[2]);
} else {
pos = (bounds.bottom + ' ' + bounds.left);
pos = (bounds[2] + ' ' + bounds[0]);
}
var node = this.createElementNS('gml:lowerCorner');
node.appendChild(this.createTextNode(pos));
@@ -399,9 +400,9 @@ ol.parser.ogc.GML_v3 = function(opt_options) {
// only 2d for simple features profile
var pos;
if (this.axisOrientation.substr(0, 2) === 'en') {
pos = (bounds.right + ' ' + bounds.top);
pos = (bounds[1] + ' ' + bounds[3]);
} else {
pos = (bounds.top + ' ' + bounds.right);
pos = (bounds[3] + ' ' + bounds[1]);
}
var node = this.createElementNS('gml:upperCorner');
node.appendChild(this.createTextNode(pos));

View File

@@ -182,8 +182,9 @@ ol.parser.XML.prototype.createElementNS = function(name, opt_uri) {
* results to a node.
*
* @param {string} name The name of a node to generate. Only use a local name.
* @param {Object} obj Structure containing data for the writer.
* @param {string=} opt_uri The name space uri to which the node belongs.
* @param {Object|string|number} obj Structure containing data for the writer.
* @param {?string=} opt_uri The name space uri to which the node
* belongs.
* @param {Element=} opt_parent Result will be appended to this node. If no
* parent is supplied, the node will not be appended to anything.
* @return {?Element} The child node.
@@ -273,3 +274,22 @@ ol.parser.XML.prototype.serialize = function(node) {
return goog.dom.xml.serialize(node);
}
};
/**
* Create a document fragment node that can be appended to another node
* created by createElementNS. This will call
* document.createDocumentFragment outside of IE. In IE, the ActiveX
* object's createDocumentFragment method is used.
*
* @return {Element} A document fragment.
*/
ol.parser.XML.prototype.createDocumentFragment = function() {
var element;
if (this.xmldom) {
element = this.xmldom.createDocumentFragment();
} else {
element = document.createDocumentFragment();
}
return element;
};