From ab40ab620858097b85b5dce5639df7e4b24d63b2 Mon Sep 17 00:00:00 2001 From: Bart van den Eijnden Date: Mon, 1 Jul 2013 13:46:41 +0200 Subject: [PATCH] Add parser for OGC Filter 1.0 and 1.1 (read/write) 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 --- src/ol/expr/expression.js | 114 +++- src/ol/parser/ogc/filter.js | 37 ++ src/ol/parser/ogc/filter_v1.js | 582 ++++++++++++++++++ src/ol/parser/ogc/filter_v1_0_0.js | 167 +++++ src/ol/parser/ogc/filter_v1_1_0.js | 217 +++++++ src/ol/parser/ogc/gml.js | 9 +- src/ol/parser/ogc/gml_v2.js | 7 +- src/ol/parser/ogc/gml_v3.js | 11 +- src/ol/parser/xml.js | 24 +- test/spec/ol/parser/ogc/filter_v1_0_0.test.js | 238 +++++++ test/spec/ol/parser/ogc/filter_v1_1_0.test.js | 230 +++++++ test/spec/ol/parser/ogc/gml_v2.test.js | 16 +- test/spec/ol/parser/ogc/gml_v3.test.js | 26 +- test/spec/ol/parser/ogc/xml/filter_v1_0_0.xml | 23 + .../ol/parser/ogc/xml/filter_v1_0_0/bbox.xml | 8 + .../ogc/xml/filter_v1_0_0/bbox_nogeom.xml | 7 + .../parser/ogc/xml/filter_v1_0_0/between.xml | 11 + .../parser/ogc/xml/filter_v1_0_0/between2.xml | 7 + .../ogc/xml/filter_v1_0_0/betweendates.xml | 11 + .../parser/ogc/xml/filter_v1_0_0/contains.xml | 12 + .../xml/filter_v1_0_0/custombetweendates.xml | 11 + .../parser/ogc/xml/filter_v1_0_0/dwithin.xml | 9 + .../ogc/xml/filter_v1_0_0/intersects.xml | 12 + .../xml/filter_v1_0_0/logicalfeatureid.xml | 10 + .../xml/filter_v1_0_0/logicalfeatureidand.xml | 10 + .../xml/filter_v1_0_0/logicalfeatureidnot.xml | 5 + .../ol/parser/ogc/xml/filter_v1_0_0/null.xml | 5 + .../parser/ogc/xml/filter_v1_0_0/within.xml | 12 + .../ol/parser/ogc/xml/filter_v1_1_0/bbox.xml | 9 + .../ogc/xml/filter_v1_1_0/bbox_nogeomname.xml | 8 + .../ogc/xml/filter_v1_1_0/customfunction.xml | 11 + .../parser/ogc/xml/filter_v1_1_0/function.xml | 10 + .../ogc/xml/filter_v1_1_0/intersects.xml | 9 + .../ogc/xml/filter_v1_1_0/likematchcase.xml | 6 + .../ogc/xml/filter_v1_1_0/nestedfunction.xml | 13 + .../parser/ogc/xml/filter_v1_1_0/sortby.xml | 10 + .../ol/parser/ogc/xml/filter_v1_1_0/test.xml | 31 + 37 files changed, 1899 insertions(+), 39 deletions(-) create mode 100644 src/ol/parser/ogc/filter.js create mode 100644 src/ol/parser/ogc/filter_v1.js create mode 100644 src/ol/parser/ogc/filter_v1_0_0.js create mode 100644 src/ol/parser/ogc/filter_v1_1_0.js create mode 100644 test/spec/ol/parser/ogc/filter_v1_0_0.test.js create mode 100644 test/spec/ol/parser/ogc/filter_v1_1_0.test.js create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox_nogeom.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/between.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/between2.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/betweendates.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/contains.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/custombetweendates.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/dwithin.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/intersects.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureid.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidand.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidnot.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/null.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_0_0/within.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox_nogeomname.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/customfunction.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/function.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/intersects.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/likematchcase.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/nestedfunction.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/sortby.xml create mode 100644 test/spec/ol/parser/ogc/xml/filter_v1_1_0/test.xml diff --git a/src/ol/expr/expression.js b/src/ol/expr/expression.js index 81a511bc26..277364d750 100644 --- a/src/ol/expr/expression.js +++ b/src/ol/expr/expression.js @@ -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.getAttributes()[opt_attribute] : this.getGeometry(); if (geometry) { intersects = ol.extent.intersects(geometry.getBounds(), [minX, maxX, minY, maxY]); @@ -142,6 +153,75 @@ ol.expr.lib[ol.expr.functions.FID] = function(var_args) { }; +/** + * 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. @@ -156,3 +236,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); +}; diff --git a/src/ol/parser/ogc/filter.js b/src/ol/parser/ogc/filter.js new file mode 100644 index 0000000000..6f1aece8fa --- /dev/null +++ b/src/ol/parser/ogc/filter.js @@ -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); diff --git a/src/ol/parser/ogc/filter_v1.js b/src/ol/parser/ogc/filter_v1.js new file mode 100644 index 0000000000..4b7f1063ec --- /dev/null +++ b/src/ol/parser/ogc/filter_v1.js @@ -0,0 +1,582 @@ +goog.provide('ol.parser.ogc.Filter_v1'); +goog.require('goog.array'); +goog.require('goog.dom.xml'); +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.Logical'); +goog.require('ol.expr.LogicalOp'); +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) { + // only the simplest of ogc:expression handled + // "some text and an attribute" + var obj, value = ''; + for (var child = node.firstChild; child; child = child.nextSibling) { + switch (child.nodeType) { + case 1: + obj = this.readNode(child); + if (obj['property']) { + value += obj['property']; + } else if (goog.isDef(obj['value'])) { + value += obj['value']; + } + break; + case 3: // text node + case 4: // cdata section + value += child.nodeValue; + break; + default: + break; + } + } + return value; + }, + '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), + 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'] = {}; + } + obj['fids'][fid] = true; + } + }, + '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'], + 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'] = isNaN(value) ? nodeValue : value; + }, + 'PropertyName': function(node, obj) { + obj['property'] = this.getChildValue(node); + }, + 'LowerBoundary': function(node, obj) { + var readers = this.readers[this.defaultNamespaceURI]; + obj['lowerBoundary'] = goog.string.toNumber( + readers['_expression'].call(this, node)); + }, + 'UpperBoundary': function(node, obj) { + var readers = this.readers[this.defaultNamespaceURI]; + obj['upperBoundary'] = goog.string.toNumber( + readers['_expression'].call(this, node)); + }, + '_spatial': function(node, obj, identifier) { + var args = [], container = {}; + this.readChildNodes(node, container); + if (goog.isDef(container.geometry)) { + args.push(this.gml_.createGeometry(container)); + } else { + args = container['bounds']; + } + if (goog.isDef(container['distance'])) { + args.push(container['distance']); + } + if (goog.isDef(container['distanceUnits'])) { + args.push(container['distanceUnits']); + } + args.push(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) { + obj['distance'] = parseInt(this.getChildValue(node), 10); + obj['distanceUnits'] = 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 fids = filter.getArgs(); + for (var i = 0, ii = fids.length; i < ii; i++) { + this.writeNode('FeatureId', fids[i], 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 value = filters[i].getRight(); + if (filters[i].getOperator() === ol.expr.ComparisonOp.GTE) { + lower = value; + } else if (filters[i].getOperator() === ol.expr.ComparisonOp.LTE) { + upper = value; + } + } + this.writeNode('LowerBoundary', lower, null, node); + this.writeNode('UpperBoundary', upper, null, node); + return node; + }, + 'PropertyName': function(name) { + var node = this.createElementNS('ogc:PropertyName'); + node.appendChild(this.createTextNode(name)); + return node; + }, + 'Literal': function(value) { + if (value instanceof Date) { + value = value.toISOString(); + } + var node = this.createElementNS('ogc:Literal'); + node.appendChild(this.createTextNode(value)); + return node; + }, + 'LowerBoundary': function(value) { + var node = this.createElementNS('ogc:LowerBoundary'); + this.writeOgcExpression(value, node); + return node; + }, + 'UpperBoundary': function(value) { + var node = this.createElementNS('ogc:UpperBoundary'); + this.writeOgcExpression(value, 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(); + node.setAttribute('units', args[2]); + node.appendChild(this.createTextNode(args[1])); + 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; + } + } + }; + this.filterMap_ = { + '&&': 'And', + '||': 'Or', + '!': 'Not', + '==': 'PropertyIsEqualTo', + '!=': 'PropertyIsNotEqualTo', + '<': 'PropertyIsLessThan', + '>': 'PropertyIsGreaterThan', + '<=': 'PropertyIsLessThanOrEqualTo', + '>=': 'PropertyIsGreaterThanOrEqualTo', + '..': 'PropertyIsBetween', + '~': 'PropertyIsLike', + 'NULL': 'PropertyIsNull', + 'BBOX': 'BBOX', + 'DWITHIN': 'DWITHIN', + 'WITHIN': 'WITHIN', + 'CONTAINS': 'CONTAINS', + 'INTERSECTS': 'INTERSECTS', + 'FID': '_featureIds' + }; + goog.base(this); +}; +goog.inherits(ol.parser.ogc.Filter_v1, ol.parser.XML); + + +/** + * @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 isNull = (type === ol.expr.ComparisonOp.EQ && + filter.getRight() === null); + if (isNull) { + type = 'NULL'; + } + var isBetween = (type === ol.expr.LogicalOp.AND && + filter.getLeft() instanceof ol.expr.Comparison && + filter.getRight() instanceof ol.expr.Comparison && + filter.getLeft().getLeft() === filter.getRight().getLeft() && + (filter.getLeft().getOperator() === ol.expr.ComparisonOp.LTE || + filter.getLeft().getOperator() === ol.expr.ComparisonOp.GTE) && + (filter.getRight().getOperator() === ol.expr.ComparisonOp.LTE || + filter.getRight().getOperator() === ol.expr.ComparisonOp.GTE)); + if (isBetween) { + type = '..'; + } + } else if (filter instanceof ol.expr.Call) { + var callee = filter.getCallee().getName(); + if (callee === ol.expr.functions.FID) { + type = 'FID'; + } + else if (callee === ol.expr.functions.IEQ) { + type = '=='; + } else if (callee === ol.expr.functions.INEQ) { + type = '!='; + } + else if (callee === ol.expr.functions.LIKE) { + type = '~'; + } + else if (callee === ol.expr.functions.CONTAINS) { + type = 'CONTAINS'; + } else if (callee === ol.expr.functions.WITHIN) { + type = 'WITHIN'; + } else if (callee === ol.expr.functions.DWITHIN) { + type = 'DWITHIN'; + } else if (callee === ol.expr.functions.EXTENT) { + type = 'BBOX'; + } else if (callee === ol.expr.functions.INTERSECTS) { + type = 'INTERSECTS'; + } + } else if (filter instanceof ol.expr.Not) { + type = '!'; + } + var filterType = this.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.Call|string|number} value 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(value, node) { + if (value instanceof ol.expr.Call) { + this.writeNode('Function', value, null, node); + } else { + this.writeNode('Literal', value, null, node); + } + return node; +}; + + +/** + * @param {ol.expr.Logical} filter The filter to retrieve the sub filters from. + * @param {Array.} 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.} 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])); + goog.array.clear(subFilters); + } + } + // 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_); + } + } +}; diff --git a/src/ol/parser/ogc/filter_v1_0_0.js b/src/ol/parser/ogc/filter_v1_0_0.js new file mode 100644 index 0000000000..7bbc07f568 --- /dev/null +++ b/src/ol/parser/ogc/filter_v1_0_0.js @@ -0,0 +1,167 @@ +goog.provide('ol.parser.ogc.Filter_v1_0_0'); + +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.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'], + node.getAttribute('wildCard'), + node.getAttribute('singleChar'), + 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(); + if (goog.isDef(property)) { + 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(); + if (goog.isDef(property)) { + 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(); + node.setAttribute('wildCard', args[2]); + node.setAttribute('singleChar', args[3]); + node.setAttribute('escape', args[4]); + 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(); + var property = args[5], bbox = [args[0], args[1], args[2], args[3]], + projection = args[4]; + // 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 (goog.isNumber(args[0])) { + bbox = [args[0], args[1], args[2], args[3]]; + projection = args[4]; + property = args[5]; + } else if (args[0] instanceof ol.geom.Geometry) { + geom = args[0]; + 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)) { + if (goog.isDef(projection)) { + child.setAttribute('srsName', projection); + } + node.appendChild(child); + } + } + return node; +}; diff --git a/src/ol/parser/ogc/filter_v1_1_0.js b/src/ol/parser/ogc/filter_v1_1_0.js new file mode 100644 index 0000000000..6cfa18ed44 --- /dev/null +++ b/src/ol/parser/ogc/filter_v1_1_0.js @@ -0,0 +1,217 @@ +goog.provide('ol.parser.ogc.Filter_v1_1_0'); + +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.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'], + node.getAttribute('wildCard'), + node.getAttribute('singleChar'), + node.getAttribute('escapeChar'), + 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(); + node.setAttribute('wildCard', args[2]); + node.setAttribute('singleChar', args[3]); + node.setAttribute('escapeChar', args[4]); + if (goog.isDefAndNotNull(args[5])) { + node.setAttribute('matchCase', args[5]); + } + 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(); + var property = args[5], bbox = [args[0], args[1], args[2], args[3]], + projection = args[4]; + // 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); + this.writeNode('SortOrder', + (sortProperty['order'] == '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 (goog.isNumber(args[0])) { + bbox = [args[0], args[1], args[2], args[3]]; + projection = args[4]; + property = args[5]; + } else if (args[0] instanceof ol.geom.Geometry) { + geom = args[0]; + 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)) { + if (goog.isDef(projection)) { + child.setAttribute('srsName', projection); + } + node.appendChild(child); + } + } + return node; +}; diff --git a/src/ol/parser/ogc/gml.js b/src/ol/parser/ogc/gml.js index f5c221655f..339ef64971 100644 --- a/src/ol/parser/ogc/gml.js +++ b/src/ol/parser/ogc/gml.js @@ -284,7 +284,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); @@ -434,7 +434,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; @@ -503,13 +503,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) { @@ -553,7 +552,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)); } diff --git a/src/ol/parser/ogc/gml_v2.js b/src/ol/parser/ogc/gml_v2.js index feea6d3b22..9b4c364c2a 100644 --- a/src/ol/parser/ogc/gml_v2.js +++ b/src/ol/parser/ogc/gml_v2.js @@ -29,6 +29,7 @@ ol.parser.ogc.GML_v2 = function(opt_options) { 'Box': function(node, container) { var coordinates = []; 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]]; } @@ -90,10 +91,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; diff --git a/src/ol/parser/ogc/gml_v3.js b/src/ol/parser/ogc/gml_v3.js index 744d75afc8..7ed61aa31c 100644 --- a/src/ol/parser/ogc/gml_v3.js +++ b/src/ol/parser/ogc/gml_v3.js @@ -55,7 +55,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; @@ -206,6 +206,7 @@ ol.parser.ogc.GML_v3 = function(opt_options) { 'Envelope': function(node, container) { var coordinates = []; 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]]; }, @@ -374,9 +375,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)); @@ -386,9 +387,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)); diff --git a/src/ol/parser/xml.js b/src/ol/parser/xml.js index e5c35f3d5c..09a4389792 100644 --- a/src/ol/parser/xml.js +++ b/src/ol/parser/xml.js @@ -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; +}; diff --git a/test/spec/ol/parser/ogc/filter_v1_0_0.test.js b/test/spec/ol/parser/ogc/filter_v1_0_0.test.js new file mode 100644 index 0000000000..3c413c75ca --- /dev/null +++ b/test/spec/ol/parser/ogc/filter_v1_0_0.test.js @@ -0,0 +1,238 @@ +goog.provide('ol.test.parser.ogc.Filter_v1_0_0'); + +describe('ol.parser.ogc.Filter_v1_0_0', function() { + + var parser = new ol.parser.ogc.Filter_v1_0_0(); + + describe('#readwrite', function() { + + it('intersects filter read / written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/intersects.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Call).to.be(true); + expect(filter.getCallee().getName()).to.eql( + ol.expr.functions.INTERSECTS); + var args = filter.getArgs(); + var geom = args[0]; + expect(geom instanceof ol.geom.Polygon).to.be(true); + expect(args[2]).to.eql('Geometry'); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('within filter read / written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/within.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Call).to.be(true); + expect(filter.getCallee().getName()).to.eql(ol.expr.functions.WITHIN); + var args = filter.getArgs(); + var geom = args[0]; + expect(geom instanceof ol.geom.Polygon).to.be(true); + expect(args[2]).to.eql('Geometry'); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('contains filter read / written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/contains.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Call).to.be(true); + expect(filter.getCallee().getName()).to.eql( + ol.expr.functions.CONTAINS); + var args = filter.getArgs(); + var geom = args[0]; + expect(geom instanceof ol.geom.Polygon).to.be(true); + expect(args[2]).to.eql('Geometry'); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('between filter read / written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/between.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Logical).to.be.ok(); + expect(filter.getOperator()).to.eql(ol.expr.LogicalOp.AND); + expect(filter.getLeft() instanceof ol.expr.Comparison).to.be.ok(); + expect(filter.getLeft().getOperator()).to.eql(ol.expr.ComparisonOp.GTE); + expect(filter.getLeft().getLeft()).to.eql('number'); + expect(filter.getLeft().getRight()).to.eql(0); + expect(filter.getRight() instanceof ol.expr.Comparison).to.be.ok(); + expect(filter.getRight().getOperator()).to.eql( + ol.expr.ComparisonOp.LTE); + expect(filter.getRight().getLeft()).to.eql('number'); + expect(filter.getRight().getRight()).to.eql(100); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('between filter read correctly without literals', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/between2.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Logical).to.be.ok(); + expect(filter.getOperator()).to.eql(ol.expr.LogicalOp.AND); + expect(filter.getLeft() instanceof ol.expr.Comparison).to.be.ok(); + expect(filter.getLeft().getOperator()).to.eql(ol.expr.ComparisonOp.GTE); + expect(filter.getLeft().getLeft()).to.eql('number'); + expect(filter.getLeft().getRight()).to.eql(0); + expect(filter.getRight() instanceof ol.expr.Comparison).to.be.ok(); + expect(filter.getRight().getOperator()).to.eql( + ol.expr.ComparisonOp.LTE); + expect(filter.getRight().getLeft()).to.eql('number'); + expect(filter.getRight().getRight()).to.eql(100); + done(); + }); + }); + + it('null filter read / written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/null.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Comparison).to.be.ok(); + expect(filter.getLeft()).to.eql('prop'); + expect(filter.getRight()).to.eql(null); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('BBOX written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/bbox.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Call( + new ol.expr.Identifier(ol.expr.functions.EXTENT), + [-180, -90, 180, 90, 'EPSG:4326', 'the_geom']); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('BBOX without geometry name written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/bbox_nogeom.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Call( + new ol.expr.Identifier(ol.expr.functions.EXTENT), + [-180, -90, 180, 90, 'EPSG:4326']); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('DWithin written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/dwithin.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Call(new ol.expr.Identifier( + ol.expr.functions.DWITHIN), + [new ol.geom.Point([2488789, 289552]), 1000, 'm', undefined, + 'Geometry']); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + filter = parser.read(xml); + output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + }); + + // the Filter Encoding spec doesn't allow for FID filters inside logical + // filters however, to be liberal, we will write them without complaining + describe('#logicalfid', function() { + + it('logical filter [OR] with fid filter written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureid.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Logical(ol.expr.LogicalOp.OR, + new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.LIKE), + ['person', 'me', '*', '.', '!']), + new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.FID), + ['foo.1', 'foo.2'])); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('logical filter [AND] with fid filter written correctly', + function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/' + + 'logicalfeatureidand.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Logical(ol.expr.LogicalOp.AND, + new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.LIKE), + ['person', 'me', '*', '.', '!']), + new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.FID), + ['foo.1', 'foo.2'])); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('logical filter [NOT] with fid filter written correctly', + function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/' + + 'logicalfeatureidnot.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Not( + new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.FID), + ['foo.2'])); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + }); + + describe('#date', function() { + + it('date writing works as expected', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_0_0/betweendates.xml'; + afterLoadXml(url, function(xml) { + // ISO 8601: 2010-11-27T18:19:15.123Z + var start = new Date(Date.UTC(2010, 10, 27, 18, 19, 15, 123)); + // ISO 8601: 2011-12-27T18:19:15.123Z + var end = new Date(Date.UTC(2011, 11, 27, 18, 19, 15, 123)); + var filter = new ol.expr.Logical(ol.expr.LogicalOp.AND, + new ol.expr.Comparison(ol.expr.ComparisonOp.GTE, 'when', start), + new ol.expr.Comparison(ol.expr.ComparisonOp.LTE, 'when', end)); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + }); + +}); + +goog.require('goog.dom.xml'); +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.Logical'); +goog.require('ol.expr.LogicalOp'); +goog.require('ol.expr.Not'); +goog.require('ol.expr.functions'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.parser.ogc.Filter_v1_0_0'); diff --git a/test/spec/ol/parser/ogc/filter_v1_1_0.test.js b/test/spec/ol/parser/ogc/filter_v1_1_0.test.js new file mode 100644 index 0000000000..695a848d65 --- /dev/null +++ b/test/spec/ol/parser/ogc/filter_v1_1_0.test.js @@ -0,0 +1,230 @@ +goog.provide('ol.test.parser.ogc.Filter_v1_1_0'); + +describe('ol.parser.ogc.Filter_v1_1_0', function() { + + var parser = new ol.parser.ogc.Filter_v1_1_0(); + + describe('#readwrite', function() { + + it('filter read correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/test.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + expect(filter instanceof ol.expr.Logical).to.be(true); + expect(filter.getOperator()).to.eql(ol.expr.LogicalOp.OR); + var filters = []; + parser.getSubfiltersForLogical_(filter, filters); + expect(filters.length).to.eql(5); + expect(filters[0]).to.eql(new ol.expr.Logical(ol.expr.LogicalOp.AND, + new ol.expr.Comparison( + ol.expr.ComparisonOp.GTE, 'number', 1064866676), + new ol.expr.Comparison( + ol.expr.ComparisonOp.LTE, 'number', 1065512599))); + expect(filters[1]).to.eql(new ol.expr.Not(new ol.expr.Comparison( + ol.expr.ComparisonOp.LTE, 'FOO', 5000))); + expect(filters[2] instanceof ol.expr.Call).to.be(true); + expect(filters[2].getCallee().getName()).to.eql( + ol.expr.functions.LIKE); + expect(filters[2].getArgs()).to.eql(['cat', '*dog.food!*good', '*', + '.', '!', null]); + expect(filters[3] instanceof ol.expr.Call).to.be(true); + expect(filters[3].getCallee().getName()).to.eql( + ol.expr.functions.IEQ); + expect(filters[3].getArgs()).to.eql(['cat', 'dog']); + expect(filters[4] instanceof ol.expr.Comparison).to.be(true); + expect(filters[4].getOperator()).to.eql(ol.expr.ComparisonOp.EQ); + expect(filters[4].getLeft()).to.eql('cat'); + expect(filters[4].getRight()).to.eql('dog'); + done(); + }); + }); + + it('matchCase read correctly', function() { + var cases = [{ + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: true + }, { + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: true + }, { + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: true + }, { + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: false + }, { + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: false + }, { + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: true + }, { + str: + '' + + '' + + 'cat' + + 'dog' + + '' + + '', + exp: false + }]; + var filter, c; + for (var i = 0; i < cases.length; ++i) { + c = cases[i]; + filter = parser.read(c.str); + var matchCase = (filter instanceof ol.expr.Call) ? false : true; + expect(matchCase).to.eql(c.exp); + } + }); + + it('BBOX filter written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/bbox.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('BBOX filter without property name written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/bbox_nogeomname.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('Intersects filter read / written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/intersects.xml'; + afterLoadXml(url, function(xml) { + var filter = parser.read(xml); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('Filter functions written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/function.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Call(new ol.expr.Identifier( + ol.expr.functions.INTERSECTS), + [new ol.expr.Call(new ol.expr.Identifier('querySingle'), + ['sf:restricted', 'the_geom', + 'cat=3']), undefined, 'the_geom']); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('Custom filter functions written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/customfunction.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Logical(ol.expr.LogicalOp.AND, + new ol.expr.Call(new ol.expr.Identifier(ol.expr.functions.INEQ), + ['FOO', new ol.expr.Call(new ol.expr.Identifier('customFunction'), + ['param1', 'param2'])])); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('Nested filter functions written correctly', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/nestedfunction.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Call(new ol.expr.Identifier( + ol.expr.functions.DWITHIN), + [new ol.expr.Call(new ol.expr.Identifier('collectGeometries'), + [new ol.expr.Call(new ol.expr.Identifier('queryCollection'), + ['sf:roads', 'the_geom', 'INCLUDE'])]), 200, 'meters', + undefined, 'the_geom']); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('matchCase written correctly on Like filter', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/likematchcase.xml'; + afterLoadXml(url, function(xml) { + var filter = new ol.expr.Call( + new ol.expr.Identifier(ol.expr.functions.LIKE), + ['person', '*me*', '*', '.', '!', false]); + var output = parser.write(filter); + expect(goog.dom.xml.loadXml(output)).to.xmleql(xml); + done(); + }); + }); + + it('sortBy written correctly on Like filter', function(done) { + var url = 'spec/ol/parser/ogc/xml/filter_v1_1_0/sortby.xml'; + afterLoadXml(url, function(xml) { + var writer = parser.writers['http://www.opengis.net/ogc']['SortBy']; + var output = writer.call(parser, [{ + 'property': 'Title', + 'order': 'ASC' + },{ + 'property': 'Relevance', + 'order': 'DESC' + }]); + expect(output).to.xmleql(xml); + done(); + }); + }); + + }); + +}); + +goog.require('goog.dom.xml'); +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.Logical'); +goog.require('ol.expr.LogicalOp'); +goog.require('ol.expr.Not'); +goog.require('ol.expr.functions'); +goog.require('ol.parser.ogc.Filter_v1_1_0'); diff --git a/test/spec/ol/parser/ogc/gml_v2.test.js b/test/spec/ol/parser/ogc/gml_v2.test.js index cfa4d82859..f57f1cbbd9 100644 --- a/test/spec/ol/parser/ogc/gml_v2.test.js +++ b/test/spec/ol/parser/ogc/gml_v2.test.js @@ -19,7 +19,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -47,7 +47,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -76,7 +76,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -104,7 +104,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -138,7 +138,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -170,7 +170,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -188,7 +188,7 @@ describe('ol.parser.gml_v2', function() { var p = new ol.parser.ogc.GML_v2({srsName: 'foo', featureNS: 'http://foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); @@ -231,7 +231,7 @@ describe('ol.parser.gml_v2', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); diff --git a/test/spec/ol/parser/ogc/gml_v3.test.js b/test/spec/ol/parser/ogc/gml_v3.test.js index b58878b0c4..5586181d0d 100644 --- a/test/spec/ol/parser/ogc/gml_v3.test.js +++ b/test/spec/ol/parser/ogc/gml_v3.test.js @@ -17,7 +17,7 @@ describe('ol.parser.gml_v3', function() { var url = 'spec/ol/parser/ogc/xml/gml_v3/linearring.xml'; afterLoadXml(url, function(xml) { var obj = parser.read(xml); - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); parser.srsName = 'foo'; var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; @@ -33,7 +33,7 @@ describe('ol.parser.gml_v3', function() { var url = 'spec/ol/parser/ogc/xml/gml_v3/linestring.xml'; afterLoadXml(url, function(xml) { var obj = parser.read(xml); - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); parser.srsName = 'foo'; var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; @@ -59,7 +59,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var p = new ol.parser.ogc.GML_v3({curve: true, srsName: 'foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); @@ -84,7 +84,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var p = new ol.parser.ogc.GML_v3({multiCurve: false, srsName: 'foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); @@ -99,7 +99,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -116,7 +116,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var p = new ol.parser.ogc.GML_v3({curve: true, srsName: 'foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); @@ -143,7 +143,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -170,7 +170,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var p = new ol.parser.ogc.GML_v3({multiSurface: false, srsName: 'foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); @@ -195,7 +195,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var obj = parser.read(xml); parser.srsName = 'foo'; - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; expect(goog.dom.xml.loadXml(parser.serialize(node))).to.xmleql(xml); @@ -211,7 +211,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var p = new ol.parser.ogc.GML_v3({surface: true, srsName: 'foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); @@ -225,7 +225,7 @@ describe('ol.parser.gml_v3', function() { var url = 'spec/ol/parser/ogc/xml/gml_v3/point.xml'; afterLoadXml(url, function(xml) { var obj = parser.read(xml); - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); parser.srsName = 'foo'; var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; @@ -240,7 +240,7 @@ describe('ol.parser.gml_v3', function() { var url = 'spec/ol/parser/ogc/xml/gml_v3/polygon.xml'; afterLoadXml(url, function(xml) { var obj = parser.read(xml); - var geom = parser.createGeometry_({geometry: obj.geometry}); + var geom = parser.createGeometry({geometry: obj.geometry}); parser.srsName = 'foo'; var node = parser.featureNSWiters_['_geometry'].apply(parser, [geom]).firstChild; @@ -263,7 +263,7 @@ describe('ol.parser.gml_v3', function() { afterLoadXml(url, function(xml) { var p = new ol.parser.ogc.GML_v3({surface: true, srsName: 'foo'}); var obj = p.read(xml); - var geom = p.createGeometry_({geometry: obj.geometry}); + var geom = p.createGeometry({geometry: obj.geometry}); var node = p.featureNSWiters_['_geometry'].apply(p, [geom]).firstChild; expect(goog.dom.xml.loadXml(p.serialize(node))).to.xmleql(xml); diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0.xml new file mode 100644 index 0000000000..5aea430597 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0.xml @@ -0,0 +1,23 @@ + + + + number + + 1064866676 + + + 1065512599 + + + + cat + *dog.food!*good + + + + FOO + 5000 + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox.xml new file mode 100644 index 0000000000..48d6f7f118 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox.xml @@ -0,0 +1,8 @@ + + + the_geom + + -180,-90 180,90 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox_nogeom.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox_nogeom.xml new file mode 100644 index 0000000000..d763209eda --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/bbox_nogeom.xml @@ -0,0 +1,7 @@ + + + + -180,-90 180,90 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/between.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/between.xml new file mode 100644 index 0000000000..1108065c53 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/between.xml @@ -0,0 +1,11 @@ + + + number + + 0 + + + 100 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/between2.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/between2.xml new file mode 100644 index 0000000000..3655c7a877 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/between2.xml @@ -0,0 +1,7 @@ + + + number + 0 + 100 + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/betweendates.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/betweendates.xml new file mode 100644 index 0000000000..3ad5b0131e --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/betweendates.xml @@ -0,0 +1,11 @@ + + + when + + 2010-11-27T18:19:15.123Z + + + 2011-12-27T18:19:15.123Z + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/contains.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/contains.xml new file mode 100644 index 0000000000..98b99ce04d --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/contains.xml @@ -0,0 +1,12 @@ + + + Geometry + + + + 2488789,289552 2588789,289552 2588789,389552 2488789,389552 2488789,289552 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/custombetweendates.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/custombetweendates.xml new file mode 100644 index 0000000000..f12024a9dd --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/custombetweendates.xml @@ -0,0 +1,11 @@ + + + when + + 2010-11-27 + + + 2011-12-27 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/dwithin.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/dwithin.xml new file mode 100644 index 0000000000..9b71143a30 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/dwithin.xml @@ -0,0 +1,9 @@ + + + Geometry + + 2488789,289552 + + 1000 + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/intersects.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/intersects.xml new file mode 100644 index 0000000000..6f34074a11 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/intersects.xml @@ -0,0 +1,12 @@ + + + Geometry + + + + 2488789,289552 2588789,289552 2588789,389552 2488789,389552 2488789,289552 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureid.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureid.xml new file mode 100644 index 0000000000..85e7b8f3c0 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureid.xml @@ -0,0 +1,10 @@ + + + + person + me + + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidand.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidand.xml new file mode 100644 index 0000000000..446197772a --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidand.xml @@ -0,0 +1,10 @@ + + + + person + me + + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidnot.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidnot.xml new file mode 100644 index 0000000000..94ad30468f --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/logicalfeatureidnot.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/null.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/null.xml new file mode 100644 index 0000000000..bc0d304e64 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/null.xml @@ -0,0 +1,5 @@ + + + prop + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_0_0/within.xml b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/within.xml new file mode 100644 index 0000000000..820ea450fc --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_0_0/within.xml @@ -0,0 +1,12 @@ + + + Geometry + + + + 2488789,289552 2588789,289552 2588789,389552 2488789,389552 2488789,289552 + + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox.xml new file mode 100644 index 0000000000..9974500d3d --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox.xml @@ -0,0 +1,9 @@ + + + the_geom + + -180 -90 + 180 90 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox_nogeomname.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox_nogeomname.xml new file mode 100644 index 0000000000..29a08f44c9 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/bbox_nogeomname.xml @@ -0,0 +1,8 @@ + + + + -180 -90 + 180 90 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/customfunction.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/customfunction.xml new file mode 100644 index 0000000000..cebdc718cc --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/customfunction.xml @@ -0,0 +1,11 @@ + + + + FOO + + param1 + param2 + + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/function.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/function.xml new file mode 100644 index 0000000000..749508778f --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/function.xml @@ -0,0 +1,10 @@ + + + the_geom + + sf:restricted + the_geom + cat=3 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/intersects.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/intersects.xml new file mode 100644 index 0000000000..b2b537638b --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/intersects.xml @@ -0,0 +1,9 @@ + + + Geometry + + -180 -90 + 180 90 + + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/likematchcase.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/likematchcase.xml new file mode 100644 index 0000000000..97225dfcdc --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/likematchcase.xml @@ -0,0 +1,6 @@ + + + person + *me* + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/nestedfunction.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/nestedfunction.xml new file mode 100644 index 0000000000..9771d40de7 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/nestedfunction.xml @@ -0,0 +1,13 @@ + + + the_geom + + + sf:roads + the_geom + INCLUDE + + + 200 + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/sortby.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/sortby.xml new file mode 100644 index 0000000000..c229843628 --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/sortby.xml @@ -0,0 +1,10 @@ + + + Title + ASC + + + Relevance + DESC + + diff --git a/test/spec/ol/parser/ogc/xml/filter_v1_1_0/test.xml b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/test.xml new file mode 100644 index 0000000000..f025e4d3be --- /dev/null +++ b/test/spec/ol/parser/ogc/xml/filter_v1_1_0/test.xml @@ -0,0 +1,31 @@ + + + + number + + 1064866676 + + + 1065512599 + + + + cat + *dog.food!*good + + + + FOO + 5000 + + + + cat + dog + + + cat + dog + + +