Verify GetFeature with more complex filter

This commit is contained in:
Björn Harrtell
2020-08-10 21:30:06 +02:00
parent a58bff0981
commit 01f355c37f
5 changed files with 217 additions and 28 deletions
+84 -24
View File
@@ -3,6 +3,7 @@
*/ */
import GML2 from './GML2.js'; import GML2 from './GML2.js';
import GML3 from './GML3.js'; import GML3 from './GML3.js';
import GML32 from './GML32.js';
import GMLBase, {GMLNS} from './GMLBase.js'; import GMLBase, {GMLNS} from './GMLBase.js';
import XMLFeature from './XMLFeature.js'; import XMLFeature from './XMLFeature.js';
import { import {
@@ -225,6 +226,15 @@ const SCHEMA_LOCATIONS = {
'http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd', 'http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.0.0/wfs.xsd',
}; };
/**
* @type {Object<string, object>}
*/
const GML_FORMATS = {
'2.0.0': GML32,
'1.1.0': GML3,
'1.0.0': GML2,
};
/** /**
* @const * @const
* @type {string} * @type {string}
@@ -271,7 +281,9 @@ class WFS extends XMLFeature {
* @private * @private
* @type {GMLBase} * @type {GMLBase}
*/ */
this.gmlFormat_ = options.gmlFormat ? options.gmlFormat : new GML3(); this.gmlFormat_ = options.gmlFormat
? options.gmlFormat
: new GML_FORMATS[this.version_]();
/** /**
* @private * @private
@@ -620,7 +632,14 @@ function createTransactionRequest(node, baseObj, version, options) {
const featurePrefix = options.featurePrefix const featurePrefix = options.featurePrefix
? options.featurePrefix ? options.featurePrefix
: FEATURE_PREFIX; : FEATURE_PREFIX;
const gmlVersion = version === '1.0.0' ? 2 : 3; let gmlVersion;
if (version === '1.0.0') {
gmlVersion = 2;
} else if (version === '1.1.0') {
gmlVersion = 3;
} else if (version === '2.0.0') {
gmlVersion = 3.2;
}
const obj = assign( const obj = assign(
{node}, {node},
{ {
@@ -723,8 +742,10 @@ function writeFeature(node, feature, objectStack) {
node.appendChild(child); node.appendChild(child);
if (gmlVersion === 2) { if (gmlVersion === 2) {
GML2.prototype.writeFeatureElement(child, feature, objectStack); GML2.prototype.writeFeatureElement(child, feature, objectStack);
} else { } else if (gmlVersion === 3) {
GML3.prototype.writeFeatureElement(child, feature, objectStack); GML3.prototype.writeFeatureElement(child, feature, objectStack);
} else {
GML32.prototype.writeFeatureElement(child, feature, objectStack);
} }
} }
@@ -853,8 +874,10 @@ function writeProperty(node, pair, objectStack) {
) { ) {
if (gmlVersion === 2) { if (gmlVersion === 2) {
GML2.prototype.writeGeometryElement(value, pair.value, objectStack); GML2.prototype.writeGeometryElement(value, pair.value, objectStack);
} else { } else if (gmlVersion === 3) {
GML3.prototype.writeGeometryElement(value, pair.value, objectStack); GML3.prototype.writeGeometryElement(value, pair.value, objectStack);
} else {
GML32.prototype.writeGeometryElement(value, pair.value, objectStack);
} }
} else { } else {
writeStringTextNode(value, pair.value); writeStringTextNode(value, pair.value);
@@ -915,6 +938,7 @@ const GETFEATURE_SERIALIZERS = {
'Not': makeChildAppender(writeNotFilter), 'Not': makeChildAppender(writeNotFilter),
'BBOX': makeChildAppender(writeBboxFilter), 'BBOX': makeChildAppender(writeBboxFilter),
'Contains': makeChildAppender(writeContainsFilter), 'Contains': makeChildAppender(writeContainsFilter),
'Disjoint': makeChildAppender(writeDisjointFilter),
'Intersects': makeChildAppender(writeIntersectsFilter), 'Intersects': makeChildAppender(writeIntersectsFilter),
'ResourceId': makeChildAppender(writeResourceIdFilter), 'ResourceId': makeChildAppender(writeResourceIdFilter),
'Within': makeChildAppender(writeWithinFilter), 'Within': makeChildAppender(writeWithinFilter),
@@ -1009,11 +1033,12 @@ function writeFilterCondition(node, filter, objectStack) {
function writeBboxFilter(node, filter, objectStack) { function writeBboxFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName; parent['srsName'] = filter.srsName;
const ns = OGCNS[context['version']]; const format = GML_FORMATS[version];
writeOgcPropertyName(ns, node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.extent, objectStack); format.prototype.writeGeometryElement(node, filter.extent, objectStack);
} }
/** /**
@@ -1024,11 +1049,12 @@ function writeBboxFilter(node, filter, objectStack) {
function writeContainsFilter(node, filter, objectStack) { function writeContainsFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName; parent['srsName'] = filter.srsName;
const ns = OGCNS[context['version']]; const format = GML_FORMATS[version];
writeOgcPropertyName(ns, node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
} }
/** /**
@@ -1039,11 +1065,28 @@ function writeContainsFilter(node, filter, objectStack) {
function writeIntersectsFilter(node, filter, objectStack) { function writeIntersectsFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName; parent['srsName'] = filter.srsName;
const ns = OGCNS[context['version']]; const format = GML_FORMATS[version];
writeOgcPropertyName(ns, node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
}
/**
* @param {Node} node Node.
* @param {import("./filter/Disjoint.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack.
*/
function writeDisjointFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writePropertyName(version, node, filter.geometryName);
format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
} }
/** /**
@@ -1063,11 +1106,12 @@ function writeResourceIdFilter(node, filter, objectStack) {
function writeWithinFilter(node, filter, objectStack) { function writeWithinFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName; parent['srsName'] = filter.srsName;
const ns = OGCNS[context['version']]; const format = GML_FORMATS[version];
writeOgcPropertyName(ns, node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
} }
/** /**
@@ -1150,11 +1194,12 @@ function writeNotFilter(node, filter, objectStack) {
function writeComparisonFilter(node, filter, objectStack) { function writeComparisonFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version'];
const ns = OGCNS[context['version']]; const ns = OGCNS[context['version']];
if (filter.matchCase !== undefined) { if (filter.matchCase !== undefined) {
node.setAttribute('matchCase', filter.matchCase.toString()); node.setAttribute('matchCase', filter.matchCase.toString());
} }
writeOgcPropertyName(ns, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
writeOgcLiteral(ns, node, '' + filter.expression); writeOgcLiteral(ns, node, '' + filter.expression);
} }
@@ -1166,8 +1211,8 @@ function writeComparisonFilter(node, filter, objectStack) {
function writeIsNullFilter(node, filter, objectStack) { function writeIsNullFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const ns = OGCNS[context['version']]; const version = context['version'];
writeOgcPropertyName(ns, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
} }
/** /**
@@ -1178,9 +1223,10 @@ function writeIsNullFilter(node, filter, objectStack) {
function writeIsBetweenFilter(node, filter, objectStack) { function writeIsBetweenFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const version = context['version'];
const ns = OGCNS[context['version']]; const ns = OGCNS[context['version']];
writeOgcPropertyName(ns, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
const lowerBoundary = createElementNS(ns, 'LowerBoundary'); const lowerBoundary = createElementNS(ns, 'LowerBoundary');
node.appendChild(lowerBoundary); node.appendChild(lowerBoundary);
@@ -1199,14 +1245,15 @@ function writeIsBetweenFilter(node, filter, objectStack) {
function writeIsLikeFilter(node, filter, objectStack) { function writeIsLikeFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context']; const context = parent['context'];
const ns = OGCNS[context['version']]; const version = context['version'];
const ns = OGCNS[version];
node.setAttribute('wildCard', filter.wildCard); node.setAttribute('wildCard', filter.wildCard);
node.setAttribute('singleChar', filter.singleChar); node.setAttribute('singleChar', filter.singleChar);
node.setAttribute('escapeChar', filter.escapeChar); node.setAttribute('escapeChar', filter.escapeChar);
if (filter.matchCase !== undefined) { if (filter.matchCase !== undefined) {
node.setAttribute('matchCase', filter.matchCase.toString()); node.setAttribute('matchCase', filter.matchCase.toString());
} }
writeOgcPropertyName(ns, node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
writeOgcLiteral(ns, node, '' + filter.pattern); writeOgcLiteral(ns, node, '' + filter.pattern);
} }
@@ -1222,13 +1269,26 @@ function writeOgcExpression(ns, tagName, node, value) {
node.appendChild(property); node.appendChild(property);
} }
/**
* @param {string} version Version.
* @param {Node} node Node.
* @param {string} value PropertyName value.
*/
function writePropertyName(version, node, value) {
if (version === '2.0.0') {
writeFesValueReference(FESNS[version], node, value);
} else {
writeOgcExpression(OGCNS[version], 'PropertyName', node, value);
}
}
/** /**
* @param {string} ns Namespace. * @param {string} ns Namespace.
* @param {Node} node Node. * @param {Node} node Node.
* @param {string} value PropertyName value. * @param {string} value PropertyName value.
*/ */
function writeOgcPropertyName(ns, node, value) { function writeFesValueReference(ns, node, value) {
writeOgcExpression(ns, 'PropertyName', node, value); writeOgcExpression(ns, 'ValueReference', node, value);
} }
/** /**
+16
View File
@@ -4,6 +4,7 @@
import And from './filter/And.js'; import And from './filter/And.js';
import Bbox from './filter/Bbox.js'; import Bbox from './filter/Bbox.js';
import Contains from './filter/Contains.js'; import Contains from './filter/Contains.js';
import Disjoint from './filter/Disjoint.js';
import During from './filter/During.js'; import During from './filter/During.js';
import EqualTo from './filter/EqualTo.js'; import EqualTo from './filter/EqualTo.js';
import GreaterThan from './filter/GreaterThan.js'; import GreaterThan from './filter/GreaterThan.js';
@@ -100,6 +101,21 @@ export function intersects(geometryName, geometry, opt_srsName) {
return new Intersects(geometryName, geometry, opt_srsName); return new Intersects(geometryName, geometry, opt_srsName);
} }
/**
* Create a `<Disjoint>` operator to test whether a geometry-valued property
* is disjoint to a given geometry.
*
* @param {!string} geometryName Geometry name to use.
* @param {!import("../geom/Geometry.js").default} geometry Geometry.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @returns {!Disjoint} `<Disjoint>` operator.
* @api
*/
export function disjoint(geometryName, geometry, opt_srsName) {
return new Disjoint(geometryName, geometry, opt_srsName);
}
/** /**
* Create a `<Within>` operator to test whether a geometry-valued property * Create a `<Within>` operator to test whether a geometry-valued property
* is within a given geometry. * is within a given geometry.
+24
View File
@@ -0,0 +1,24 @@
/**
* @module ol/format/filter/Disjoint
*/
import Spatial from './Spatial.js';
/**
* @classdesc
* Represents a `<Disjoint>` operator to test whether a geometry-valued property
* is disjoint to a given geometry.
* @api
*/
class Disjoint extends Spatial {
/**
* @param {!string} geometryName Geometry name to use.
* @param {!import("../../geom/Geometry.js").default} geometry Geometry.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
*/
constructor(geometryName, geometry, opt_srsName) {
super('Disjoint', geometryName, geometry, opt_srsName);
}
}
export default Disjoint;
+52 -4
View File
@@ -1,5 +1,6 @@
import Feature from '../../../../src/ol/Feature.js'; import Feature from '../../../../src/ol/Feature.js';
import GML2 from '../../../../src/ol/format/GML2.js'; import GML2 from '../../../../src/ol/format/GML2.js';
import GML32 from '../../../../src/ol/format/GML32.js';
import LineString from '../../../../src/ol/geom/LineString.js'; import LineString from '../../../../src/ol/geom/LineString.js';
import MultiLineString from '../../../../src/ol/geom/MultiLineString.js'; import MultiLineString from '../../../../src/ol/geom/MultiLineString.js';
import MultiPoint from '../../../../src/ol/geom/MultiPoint.js'; import MultiPoint from '../../../../src/ol/geom/MultiPoint.js';
@@ -16,6 +17,7 @@ import {
bbox as bboxFilter, bbox as bboxFilter,
between as betweenFilter, between as betweenFilter,
contains as containsFilter, contains as containsFilter,
disjoint as disjointFilter,
during as duringFilter, during as duringFilter,
equalTo as equalToFilter, equalTo as equalToFilter,
greaterThan as greaterThanFilter, greaterThan as greaterThanFilter,
@@ -1386,14 +1388,32 @@ describe('ol.format.WFS', function () {
describe('WFS 2.0.0', function () { describe('WFS 2.0.0', function () {
let getFeatureXml; let getFeatureXml;
let getFeatureXml2;
before(function (done) { before(function (done) {
afterLoadText('spec/ol/format/wfs/2.0.0/GetFeature.xml', function (xml) { proj4.defs(
getFeatureXml = xml; 'http://www.opengis.net/def/crs/EPSG/0/26713',
done(); '+proj=utm +zone=13 +ellps=clrk66 +datum=NAD27 +units=m +no_defs'
);
register(proj4);
afterLoadText('spec/ol/format/wfs/2.0.0/GetFeature2.xml', function (xml) {
getFeatureXml2 = xml;
afterLoadText('spec/ol/format/wfs/2.0.0/GetFeature.xml', function (
xml
) {
getFeatureXml = xml;
done();
});
}); });
}); });
it('GetFeature', function () { after(function () {
delete proj4.defs['http://www.opengis.net/def/crs/EPSG/0/26713'];
clearAllProjections();
addCommon();
});
it('can writeGetFeature query with simple resourceId filter', function () {
const wfs = new WFS({ const wfs = new WFS({
version: '2.0.0', version: '2.0.0',
}); });
@@ -1406,5 +1426,33 @@ describe('ol.format.WFS', function () {
}); });
expect(serialized).to.xmleql(parse(getFeatureXml)); expect(serialized).to.xmleql(parse(getFeatureXml));
}); });
it('can writeGetFeature query with negated disjoint spatial filter', function () {
const wfs = new WFS({
version: '2.0.0',
});
const geometryNode = parse(
`<gml:Polygon xmlns:gml="http://www.opengis.net/gml/3.2"
srsName='http://www.opengis.net/def/crs/EPSG/0/26713'>
<gml:exterior>
<gml:LinearRing>
<!-- pairs must form a closed ring -->
<gml:posList>590431 4915204 590430
4915205 590429 4915204 590430
4915203 590431 4915204</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>`
);
const geometry = new GML32().readGeometryElement(geometryNode, [{}]);
const filter = notFilter(disjointFilter('sf:the_geom', geometry));
const serialized = wfs.writeGetFeature({
featureNS: 'http://www.openplans.org/spearfish',
featureTypes: ['bugsites'],
featurePrefix: 'sf',
filter,
});
expect(serialized).to.xmleql(parse(getFeatureXml2));
});
}); });
}); });
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This example demonstrates a WFS 2.0 GetFeature POST request.
WFS 2.0 does not depend on any one GML version and thus
requires an explicit namespace and schemaLocation for GML.
This spatial filter selects a single feature with
gml:id="bugsites.2".
See also:
WFS Standard: http://www.opengeospatial.org/standards/wfs
Filter Encoding Standard: http://www.opengeospatial.org/standards/filter
-->
<wfs:GetFeature service="WFS" version="2.0.0"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:fes="http://www.opengis.net/fes/2.0"
xmlns:sf="http://www.openplans.org/spearfish"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd">
<wfs:Query typeNames="sf:bugsites">
<fes:Filter>
<fes:Not>
<fes:Disjoint>
<fes:ValueReference>sf:the_geom</fes:ValueReference>
<!-- gml:id is mandatory on GML 3.2 geometry elements -->
<gml:Polygon xmlns:gml="http://www.opengis.net/gml/3.2">
<gml:exterior>
<gml:LinearRing>
<!-- pairs must form a closed ring -->
<gml:posList srsDimension="2">590431 4915204 590430
4915205 590429 4915204 590430
4915203 590431 4915204</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</fes:Disjoint>
</fes:Not>
</fes:Filter>
</wfs:Query>
</wfs:GetFeature>