Merge pull request #11419 from bjornharrtell/wfs2

WFS 2.0.0 support
This commit is contained in:
Tim Schaub
2020-08-13 05:07:53 -06:00
committed by GitHub
6 changed files with 635 additions and 143 deletions
+6 -2
View File
@@ -150,7 +150,11 @@ class GMLBase extends XMLFeature {
objectStack, objectStack,
this this
); );
} else if (localName == 'featureMembers' || localName == 'featureMember') { } else if (
localName == 'featureMembers' ||
localName == 'featureMember' ||
localName == 'member'
) {
const context = objectStack[0]; const context = objectStack[0];
let featureType = context['featureType']; let featureType = context['featureType'];
let featureNS = context['featureNS']; let featureNS = context['featureNS'];
@@ -214,7 +218,7 @@ class GMLBase extends XMLFeature {
} }
parsersNS[featureNS[p]] = parsers; parsersNS[featureNS[p]] = parsers;
} }
if (localName == 'featureMember') { if (localName == 'featureMember' || localName == 'member') {
features = pushParseAndPop(undefined, parsersNS, node, objectStack); features = pushParseAndPop(undefined, parsersNS, node, objectStack);
} else { } else {
features = pushParseAndPop([], parsersNS, node, objectStack); features = pushParseAndPop([], parsersNS, node, objectStack);
+343 -140
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 {
@@ -39,6 +40,9 @@ const FEATURE_COLLECTION_PARSERS = {
'bounds' 'bounds'
), ),
}, },
'http://www.opengis.net/wfs/2.0': {
'member': makeArrayPusher(GMLBase.prototype.readFeaturesInternal),
},
}; };
/** /**
@@ -51,6 +55,11 @@ const TRANSACTION_SUMMARY_PARSERS = {
'totalUpdated': makeObjectPropertySetter(readNonNegativeInteger), 'totalUpdated': makeObjectPropertySetter(readNonNegativeInteger),
'totalDeleted': makeObjectPropertySetter(readNonNegativeInteger), 'totalDeleted': makeObjectPropertySetter(readNonNegativeInteger),
}, },
'http://www.opengis.net/wfs/2.0': {
'totalInserted': makeObjectPropertySetter(readNonNegativeInteger),
'totalUpdated': makeObjectPropertySetter(readNonNegativeInteger),
'totalDeleted': makeObjectPropertySetter(readNonNegativeInteger),
},
}; };
/** /**
@@ -65,6 +74,13 @@ const TRANSACTION_RESPONSE_PARSERS = {
), ),
'InsertResults': makeObjectPropertySetter(readInsertResults, 'insertIds'), 'InsertResults': makeObjectPropertySetter(readInsertResults, 'insertIds'),
}, },
'http://www.opengis.net/wfs/2.0': {
'TransactionSummary': makeObjectPropertySetter(
readTransactionSummary,
'transactionSummary'
),
'InsertResults': makeObjectPropertySetter(readInsertResults, 'insertIds'),
},
}; };
/** /**
@@ -74,6 +90,9 @@ const QUERY_SERIALIZERS = {
'http://www.opengis.net/wfs': { 'http://www.opengis.net/wfs': {
'PropertyName': makeChildAppender(writeStringTextNode), 'PropertyName': makeChildAppender(writeStringTextNode),
}, },
'http://www.opengis.net/wfs/2.0': {
'PropertyName': makeChildAppender(writeStringTextNode),
},
}; };
/** /**
@@ -87,14 +106,23 @@ const TRANSACTION_SERIALIZERS = {
'Property': makeChildAppender(writeProperty), 'Property': makeChildAppender(writeProperty),
'Native': makeChildAppender(writeNative), 'Native': makeChildAppender(writeNative),
}, },
'http://www.opengis.net/wfs/2.0': {
'Insert': makeChildAppender(writeFeature),
'Update': makeChildAppender(writeUpdate),
'Delete': makeChildAppender(writeDelete),
'Property': makeChildAppender(writeProperty),
'Native': makeChildAppender(writeNative),
},
}; };
/** /**
* @typedef {Object} Options * @typedef {Object} Options
* @property {Object<string, string>|string} [featureNS] The namespace URI used for features. * @property {Object<string, string>|string} [featureNS] The namespace URI used for features.
* @property {Array<string>|string} [featureType] The feature type to parse. Only used for read operations. * @property {Array<string>|string} [featureType] The feature type to parse. Only used for read operations.
* @property {GMLBase} [gmlFormat] The GML format to use to parse the response. Default is `ol/format/GML3`. * @property {GMLBase} [gmlFormat] The GML format to use to parse the response.
* Default is `ol/format/GML2` for WFS 1.0.0, `ol/format/GML3` for WFS 1.1.0 and `ol/format/GML32` for WFS 2.0.0.
* @property {string} [schemaLocation] Optional schemaLocation to use for serialization, this will override the default. * @property {string} [schemaLocation] Optional schemaLocation to use for serialization, this will override the default.
* @property {string} [version='1.1.0'] WFS version to use. Can be either `1.0.0`, `1.1.0` or `2.0.0`.
*/ */
/** /**
@@ -134,7 +162,7 @@ const TRANSACTION_SERIALIZERS = {
* a 3D layer. This will allow the Z coordinate to be included in the transaction. * a 3D layer. This will allow the Z coordinate to be included in the transaction.
* @property {Array<Object>} nativeElements Native elements. Currently not supported. * @property {Array<Object>} nativeElements Native elements. Currently not supported.
* @property {import("./GMLBase.js").Options} [gmlOptions] GML options for the WFS transaction writer. * @property {import("./GMLBase.js").Options} [gmlOptions] GML options for the WFS transaction writer.
* @property {string} [version='1.1.0'] WFS version to use for the transaction. Can be either `1.0.0` or `1.1.0`. * @property {string} [version='1.1.0'] WFS version to use for the transaction. Can be either `1.0.0`, `1.1.0` or `2.0.0`.
*/ */
/** /**
@@ -164,30 +192,53 @@ const FEATURE_PREFIX = 'feature';
const XMLNS = 'http://www.w3.org/2000/xmlns/'; const XMLNS = 'http://www.w3.org/2000/xmlns/';
/** /**
* @type {string} * @type {Object<string, string>}
*/ */
const OGCNS = 'http://www.opengis.net/ogc'; const OGCNS = {
'2.0.0': 'http://www.opengis.net/ogc/1.1',
'1.1.0': 'http://www.opengis.net/ogc',
'1.0.0': 'http://www.opengis.net/ogc',
};
/** /**
* @type {string} * @type {Object<string, string>}
*/ */
const WFSNS = 'http://www.opengis.net/wfs'; const WFSNS = {
'2.0.0': 'http://www.opengis.net/wfs/2.0',
'1.1.0': 'http://www.opengis.net/wfs',
'1.0.0': 'http://www.opengis.net/wfs',
};
/** /**
* @type {string} * @type {Object<string, string>}
*/ */
const FESNS = 'http://www.opengis.net/fes'; const FESNS = {
'2.0.0': 'http://www.opengis.net/fes/2.0',
'1.1.0': 'http://www.opengis.net/fes',
'1.0.0': 'http://www.opengis.net/fes',
};
/** /**
* @type {Object<string, string>} * @type {Object<string, string>}
*/ */
const SCHEMA_LOCATIONS = { const SCHEMA_LOCATIONS = {
'2.0.0':
'http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd',
'1.1.0': '1.1.0':
'http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd', 'http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd',
'1.0.0': '1.0.0':
'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}
@@ -198,7 +249,7 @@ const DEFAULT_VERSION = '1.1.0';
* @classdesc * @classdesc
* Feature format for reading and writing data in the WFS format. * Feature format for reading and writing data in the WFS format.
* By default, supports WFS version 1.1.0. You can pass a GML format * By default, supports WFS version 1.1.0. You can pass a GML format
* as option if you want to read a WFS that contains GML2 (WFS 1.0.0). * as option to override the default.
* Also see {@link module:ol/format/GMLBase~GMLBase} which is used by this format. * Also see {@link module:ol/format/GMLBase~GMLBase} which is used by this format.
* *
* @api * @api
@@ -212,6 +263,12 @@ class WFS extends XMLFeature {
const options = opt_options ? opt_options : {}; const options = opt_options ? opt_options : {};
/**
* @private
* @type {string}
*/
this.version_ = options.version ? options.version : DEFAULT_VERSION;
/** /**
* @private * @private
* @type {Array<string>|string|undefined} * @type {Array<string>|string|undefined}
@@ -228,7 +285,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
@@ -236,7 +295,7 @@ class WFS extends XMLFeature {
*/ */
this.schemaLocation_ = options.schemaLocation this.schemaLocation_ = options.schemaLocation
? options.schemaLocation ? options.schemaLocation
: SCHEMA_LOCATIONS[DEFAULT_VERSION]; : SCHEMA_LOCATIONS[this.version_];
} }
/** /**
@@ -262,7 +321,7 @@ class WFS extends XMLFeature {
readFeaturesFromNode(node, opt_options) { readFeaturesFromNode(node, opt_options) {
/** @type {import("../xml.js").NodeStackItem} */ /** @type {import("../xml.js").NodeStackItem} */
const context = { const context = {
node: node, node,
}; };
assign(context, { assign(context, {
'featureType': this.featureType_, 'featureType': this.featureType_,
@@ -271,12 +330,15 @@ class WFS extends XMLFeature {
assign(context, this.getReadOptions(node, opt_options ? opt_options : {})); assign(context, this.getReadOptions(node, opt_options ? opt_options : {}));
const objectStack = [context]; const objectStack = [context];
this.gmlFormat_.FEATURE_COLLECTION_PARSERS[GMLNS][ let featuresNS;
'featureMember' if (this.version_ === '2.0.0') {
] = makeArrayPusher(GMLBase.prototype.readFeaturesInternal); featuresNS = FEATURE_COLLECTION_PARSERS;
} else {
featuresNS = this.gmlFormat_.FEATURE_COLLECTION_PARSERS;
}
let features = pushParseAndPop( let features = pushParseAndPop(
[], [],
this.gmlFormat_.FEATURE_COLLECTION_PARSERS, featuresNS,
node, node,
objectStack, objectStack,
this.gmlFormat_ this.gmlFormat_
@@ -406,9 +468,9 @@ class WFS extends XMLFeature {
* @api * @api
*/ */
writeGetFeature(options) { writeGetFeature(options) {
const node = createElementNS(WFSNS, 'GetFeature'); const node = createElementNS(WFSNS[this.version_], 'GetFeature');
node.setAttribute('service', 'WFS'); node.setAttribute('service', 'WFS');
node.setAttribute('version', '1.1.0'); node.setAttribute('version', this.version_);
let filter; let filter;
if (options) { if (options) {
if (options.handle) { if (options.handle) {
@@ -455,9 +517,10 @@ class WFS extends XMLFeature {
); );
/** @type {import("../xml.js").NodeStackItem} */ /** @type {import("../xml.js").NodeStackItem} */
const context = { const context = {
node: node, node,
}; };
assign(context, { assign(context, {
'version': this.version_,
'srsName': options.srsName, 'srsName': options.srsName,
'featureNS': options.featureNS ? options.featureNS : this.featureNS_, 'featureNS': options.featureNS ? options.featureNS : this.featureNS_,
'featurePrefix': options.featurePrefix, 'featurePrefix': options.featurePrefix,
@@ -487,101 +550,41 @@ class WFS extends XMLFeature {
*/ */
writeTransaction(inserts, updates, deletes, options) { writeTransaction(inserts, updates, deletes, options) {
const objectStack = []; const objectStack = [];
const node = createElementNS(WFSNS, 'Transaction'); const version = options.version ? options.version : this.version_;
const version = options.version ? options.version : DEFAULT_VERSION; const node = createElementNS(WFSNS[version], 'Transaction');
const gmlVersion = version === '1.0.0' ? 2 : 3;
node.setAttribute('service', 'WFS'); node.setAttribute('service', 'WFS');
node.setAttribute('version', version); node.setAttribute('version', version);
let baseObj; let baseObj;
/** @type {import("../xml.js").NodeStackItem} */ /** @type {import("../xml.js").NodeStackItem} */
let obj;
if (options) { if (options) {
baseObj = options.gmlOptions ? options.gmlOptions : {}; baseObj = options.gmlOptions ? options.gmlOptions : {};
if (options.handle) { if (options.handle) {
node.setAttribute('handle', options.handle); node.setAttribute('handle', options.handle);
} }
} }
const schemaLocation = SCHEMA_LOCATIONS[version];
node.setAttributeNS( node.setAttributeNS(
XML_SCHEMA_INSTANCE_URI, XML_SCHEMA_INSTANCE_URI,
'xsi:schemaLocation', 'xsi:schemaLocation',
schemaLocation SCHEMA_LOCATIONS[version]
); );
const featurePrefix = options.featurePrefix
? options.featurePrefix const request = createTransactionRequest(node, baseObj, version, options);
: FEATURE_PREFIX;
if (inserts) { if (inserts) {
obj = assign( serializeTransactionRequest('Insert', inserts, objectStack, request);
{node: node},
{
'featureNS': options.featureNS,
'featureType': options.featureType,
'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion,
'hasZ': options.hasZ,
'srsName': options.srsName,
}
);
assign(obj, baseObj);
pushSerializeAndPop(
obj,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Insert'),
inserts,
objectStack
);
} }
if (updates) { if (updates) {
obj = assign( serializeTransactionRequest('Update', updates, objectStack, request);
{node: node},
{
'featureNS': options.featureNS,
'featureType': options.featureType,
'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion,
'hasZ': options.hasZ,
'srsName': options.srsName,
}
);
assign(obj, baseObj);
pushSerializeAndPop(
obj,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Update'),
updates,
objectStack
);
} }
if (deletes) { if (deletes) {
pushSerializeAndPop( serializeTransactionRequest('Delete', deletes, objectStack, request);
{
node: node,
'featureNS': options.featureNS,
'featureType': options.featureType,
'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion,
'srsName': options.srsName,
},
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Delete'),
deletes,
objectStack
);
} }
if (options.nativeElements) { if (options.nativeElements) {
pushSerializeAndPop( serializeTransactionRequest(
{ 'Native',
node: node,
'featureNS': options.featureNS,
'featureType': options.featureType,
'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion,
'srsName': options.srsName,
},
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory('Native'),
options.nativeElements, options.nativeElements,
objectStack objectStack,
request
); );
} }
return node; return node;
@@ -625,6 +628,57 @@ class WFS extends XMLFeature {
} }
} }
/**
* @param {Element} node Node.
* @param {*} baseObj Base object.
* @param {string} version Version.
* @param {WriteTransactionOptions} options Options.
* @return {Object} Request object.
*/
function createTransactionRequest(node, baseObj, version, options) {
const featurePrefix = options.featurePrefix
? options.featurePrefix
: FEATURE_PREFIX;
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(
{node},
{
version,
'featureNS': options.featureNS,
'featureType': options.featureType,
'featurePrefix': featurePrefix,
'gmlVersion': gmlVersion,
'hasZ': options.hasZ,
'srsName': options.srsName,
},
baseObj
);
return obj;
}
/**
* @param {string} type Request type.
* @param {Array<import("../Feature.js").default>} features Features.
* @param {Array<*>} objectStack Object stack.
* @param {Element} request Transaction Request.
*/
function serializeTransactionRequest(type, features, objectStack, request) {
pushSerializeAndPop(
request,
TRANSACTION_SERIALIZERS,
makeSimpleNodeFactory(type),
features,
objectStack
);
}
/** /**
* @param {Element} node Node. * @param {Element} node Node.
* @param {Array<*>} objectStack Object stack. * @param {Array<*>} objectStack Object stack.
@@ -644,6 +698,11 @@ const OGC_FID_PARSERS = {
return node.getAttribute('fid'); return node.getAttribute('fid');
}), }),
}, },
'http://www.opengis.net/ogc/1.1': {
'FeatureId': makeArrayPusher(function (node, objectStack) {
return node.getAttribute('fid');
}),
},
}; };
/** /**
@@ -662,6 +721,9 @@ const INSERT_RESULTS_PARSERS = {
'http://www.opengis.net/wfs': { 'http://www.opengis.net/wfs': {
'Feature': fidParser, 'Feature': fidParser,
}, },
'http://www.opengis.net/wfs/2.0': {
'Feature': fidParser,
},
}; };
/** /**
@@ -687,8 +749,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);
} }
} }
@@ -698,8 +762,11 @@ function writeFeature(node, feature, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeOgcFidFilter(node, fid, objectStack) { function writeOgcFidFilter(node, fid, objectStack) {
const filter = createElementNS(OGCNS, 'Filter'); const context = objectStack[objectStack.length - 1];
const child = createElementNS(OGCNS, 'FeatureId'); const version = context['version'];
const ns = OGCNS[version];
const filter = createElementNS(ns, 'Filter');
const child = createElementNS(ns, 'FeatureId');
filter.appendChild(child); filter.appendChild(child);
child.setAttribute('fid', /** @type {string} */ (fid)); child.setAttribute('fid', /** @type {string} */ (fid));
node.appendChild(filter); node.appendChild(filter);
@@ -749,6 +816,7 @@ function writeDelete(node, feature, objectStack) {
function writeUpdate(node, feature, objectStack) { function writeUpdate(node, feature, objectStack) {
const context = objectStack[objectStack.length - 1]; const context = objectStack[objectStack.length - 1];
assert(feature.getId() !== undefined, 27); // Features must have an id set assert(feature.getId() !== undefined, 27); // Features must have an id set
const version = context['version'];
const featureType = context['featureType']; const featureType = context['featureType'];
const featurePrefix = context['featurePrefix']; const featurePrefix = context['featurePrefix'];
const featureNS = context['featureNS']; const featureNS = context['featureNS'];
@@ -775,8 +843,9 @@ function writeUpdate(node, feature, objectStack) {
} }
pushSerializeAndPop( pushSerializeAndPop(
/** @type {import("../xml.js").NodeStackItem} */ ({ /** @type {import("../xml.js").NodeStackItem} */ ({
version,
'gmlVersion': context['gmlVersion'], 'gmlVersion': context['gmlVersion'],
node: node, node,
'hasZ': context['hasZ'], 'hasZ': context['hasZ'],
'srsName': context['srsName'], 'srsName': context['srsName'],
}), }),
@@ -795,13 +864,15 @@ function writeUpdate(node, feature, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeProperty(node, pair, objectStack) { function writeProperty(node, pair, objectStack) {
const name = createElementNS(WFSNS, 'Name');
const context = objectStack[objectStack.length - 1]; const context = objectStack[objectStack.length - 1];
const version = context['version'];
const ns = WFSNS[version];
const name = createElementNS(ns, 'Name');
const gmlVersion = context['gmlVersion']; const gmlVersion = context['gmlVersion'];
node.appendChild(name); node.appendChild(name);
writeStringTextNode(name, pair.name); writeStringTextNode(name, pair.name);
if (pair.value !== undefined && pair.value !== null) { if (pair.value !== undefined && pair.value !== null) {
const value = createElementNS(WFSNS, 'Value'); const value = createElementNS(ns, 'Value');
node.appendChild(value); node.appendChild(value);
if ( if (
pair.value && pair.value &&
@@ -810,8 +881,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);
@@ -843,6 +916,9 @@ const GETFEATURE_SERIALIZERS = {
'http://www.opengis.net/wfs': { 'http://www.opengis.net/wfs': {
'Query': makeChildAppender(writeQuery), 'Query': makeChildAppender(writeQuery),
}, },
'http://www.opengis.net/wfs/2.0': {
'Query': makeChildAppender(writeQuery),
},
'http://www.opengis.net/ogc': { 'http://www.opengis.net/ogc': {
'During': makeChildAppender(writeDuringFilter), 'During': makeChildAppender(writeDuringFilter),
'And': makeChildAppender(writeLogicalFilter), 'And': makeChildAppender(writeLogicalFilter),
@@ -862,6 +938,27 @@ const GETFEATURE_SERIALIZERS = {
'PropertyIsBetween': makeChildAppender(writeIsBetweenFilter), 'PropertyIsBetween': makeChildAppender(writeIsBetweenFilter),
'PropertyIsLike': makeChildAppender(writeIsLikeFilter), 'PropertyIsLike': makeChildAppender(writeIsLikeFilter),
}, },
'http://www.opengis.net/fes/2.0': {
'During': makeChildAppender(writeDuringFilter),
'And': makeChildAppender(writeLogicalFilter),
'Or': makeChildAppender(writeLogicalFilter),
'Not': makeChildAppender(writeNotFilter),
'BBOX': makeChildAppender(writeBboxFilter),
'Contains': makeChildAppender(writeContainsFilter),
'Disjoint': makeChildAppender(writeDisjointFilter),
'Intersects': makeChildAppender(writeIntersectsFilter),
'ResourceId': makeChildAppender(writeResourceIdFilter),
'Within': makeChildAppender(writeWithinFilter),
'PropertyIsEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsNotEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsLessThan': makeChildAppender(writeComparisonFilter),
'PropertyIsLessThanOrEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsGreaterThan': makeChildAppender(writeComparisonFilter),
'PropertyIsGreaterThanOrEqualTo': makeChildAppender(writeComparisonFilter),
'PropertyIsNull': makeChildAppender(writeIsNullFilter),
'PropertyIsBetween': makeChildAppender(writeIsBetweenFilter),
'PropertyIsLike': makeChildAppender(writeIsLikeFilter),
},
}; };
/** /**
@@ -871,6 +968,7 @@ const GETFEATURE_SERIALIZERS = {
*/ */
function writeQuery(node, featureType, objectStack) { function writeQuery(node, featureType, objectStack) {
const context = /** @type {Object} */ (objectStack[objectStack.length - 1]); const context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const version = context['version'];
const featurePrefix = context['featurePrefix']; const featurePrefix = context['featurePrefix'];
const featureNS = context['featureNS']; const featureNS = context['featureNS'];
const propertyNames = context['propertyNames']; const propertyNames = context['propertyNames'];
@@ -882,7 +980,13 @@ function writeQuery(node, featureType, objectStack) {
} else { } else {
typeName = featureType; typeName = featureType;
} }
node.setAttribute('typeName', typeName); let typeNameAttr;
if (version === '2.0.0') {
typeNameAttr = 'typeNames';
} else {
typeNameAttr = 'typeName';
}
node.setAttribute(typeNameAttr, typeName);
if (srsName) { if (srsName) {
node.setAttribute('srsName', srsName); node.setAttribute('srsName', srsName);
} }
@@ -903,7 +1007,7 @@ function writeQuery(node, featureType, objectStack) {
); );
const filter = context['filter']; const filter = context['filter'];
if (filter) { if (filter) {
const child = createElementNS(OGCNS, 'Filter'); const child = createElementNS(getFilterNS(version), 'Filter');
node.appendChild(child); node.appendChild(child);
writeFilterCondition(child, filter, objectStack); writeFilterCondition(child, filter, objectStack);
} }
@@ -915,8 +1019,10 @@ function writeQuery(node, featureType, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeFilterCondition(node, filter, objectStack) { function writeFilterCondition(node, filter, objectStack) {
const context = /** @type {Object} */ (objectStack[objectStack.length - 1]);
/** @type {import("../xml.js").NodeStackItem} */ /** @type {import("../xml.js").NodeStackItem} */
const item = {node: node}; const item = {node};
assign(item, {context});
pushSerializeAndPop( pushSerializeAndPop(
item, item,
GETFEATURE_SERIALIZERS, GETFEATURE_SERIALIZERS,
@@ -932,11 +1038,14 @@ function writeFilterCondition(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeBboxFilter(node, filter, objectStack) { function writeBboxFilter(node, filter, objectStack) {
const context = objectStack[objectStack.length - 1]; const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
context['srsName'] = filter.srsName; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writeOgcPropertyName(node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.extent, objectStack); format.prototype.writeGeometryElement(node, filter.extent, objectStack);
} }
/** /**
@@ -945,11 +1054,14 @@ function writeBboxFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeContainsFilter(node, filter, objectStack) { function writeContainsFilter(node, filter, objectStack) {
const context = objectStack[objectStack.length - 1]; const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
context['srsName'] = filter.srsName; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writeOgcPropertyName(node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
} }
/** /**
@@ -958,11 +1070,39 @@ function writeContainsFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeIntersectsFilter(node, filter, objectStack) { function writeIntersectsFilter(node, filter, objectStack) {
const context = objectStack[objectStack.length - 1]; const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
context['srsName'] = filter.srsName; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writeOgcPropertyName(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);
}
/**
* @param {Element} node Element.
* @param {import("./filter/ResourceId.js").default} filter Filter.
* @param {Array<*>} objectStack Node stack.
*/
function writeResourceIdFilter(node, filter, objectStack) {
node.setAttribute('rid', /** @type {string} */ (filter.rid));
} }
/** /**
@@ -971,11 +1111,14 @@ function writeIntersectsFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeWithinFilter(node, filter, objectStack) { function writeWithinFilter(node, filter, objectStack) {
const context = objectStack[objectStack.length - 1]; const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
context['srsName'] = filter.srsName; const context = parent['context'];
const version = context['version'];
parent['srsName'] = filter.srsName;
const format = GML_FORMATS[version];
writeOgcPropertyName(node, filter.geometryName); writePropertyName(version, node, filter.geometryName);
GML3.prototype.writeGeometryElement(node, filter.geometry, objectStack); format.prototype.writeGeometryElement(node, filter.geometry, objectStack);
} }
/** /**
@@ -984,7 +1127,11 @@ function writeWithinFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeDuringFilter(node, filter, objectStack) { function writeDuringFilter(node, filter, objectStack) {
const valueReference = createElementNS(FESNS, 'ValueReference'); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
const ns = FESNS[version];
const valueReference = createElementNS(ns, 'ValueReference');
writeStringTextNode(valueReference, filter.propertyName); writeStringTextNode(valueReference, filter.propertyName);
node.appendChild(valueReference); node.appendChild(valueReference);
@@ -1007,8 +1154,11 @@ function writeDuringFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeLogicalFilter(node, filter, objectStack) { function writeLogicalFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
/** @type {import("../xml.js").NodeStackItem} */ /** @type {import("../xml.js").NodeStackItem} */
const item = {node: node}; const item = {node};
assign(item, {context});
const conditions = filter.conditions; const conditions = filter.conditions;
for (let i = 0, ii = conditions.length; i < ii; ++i) { for (let i = 0, ii = conditions.length; i < ii; ++i) {
const condition = conditions[i]; const condition = conditions[i];
@@ -1028,8 +1178,11 @@ function writeLogicalFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeNotFilter(node, filter, objectStack) { function writeNotFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
/** @type {import("../xml.js").NodeStackItem} */ /** @type {import("../xml.js").NodeStackItem} */
const item = {node: node}; const item = {node};
assign(item, {context});
const condition = filter.condition; const condition = filter.condition;
pushSerializeAndPop( pushSerializeAndPop(
item, item,
@@ -1046,11 +1199,15 @@ function writeNotFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeComparisonFilter(node, filter, objectStack) { function writeComparisonFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = 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(node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
writeOgcLiteral(node, '' + filter.expression); writeOgcLiteral(ns, node, '' + filter.expression);
} }
/** /**
@@ -1059,7 +1216,10 @@ function writeComparisonFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeIsNullFilter(node, filter, objectStack) { function writeIsNullFilter(node, filter, objectStack) {
writeOgcPropertyName(node, filter.propertyName); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
writePropertyName(version, node, filter.propertyName);
} }
/** /**
@@ -1068,15 +1228,20 @@ function writeIsNullFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeIsBetweenFilter(node, filter, objectStack) { function writeIsBetweenFilter(node, filter, objectStack) {
writeOgcPropertyName(node, filter.propertyName); const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
const version = context['version'];
const ns = OGCNS[context['version']];
const lowerBoundary = createElementNS(OGCNS, 'LowerBoundary'); writePropertyName(version, node, filter.propertyName);
const lowerBoundary = createElementNS(ns, 'LowerBoundary');
node.appendChild(lowerBoundary); node.appendChild(lowerBoundary);
writeOgcLiteral(lowerBoundary, '' + filter.lowerBoundary); writeOgcLiteral(ns, lowerBoundary, '' + filter.lowerBoundary);
const upperBoundary = createElementNS(OGCNS, 'UpperBoundary'); const upperBoundary = createElementNS(ns, 'UpperBoundary');
node.appendChild(upperBoundary); node.appendChild(upperBoundary);
writeOgcLiteral(upperBoundary, '' + filter.upperBoundary); writeOgcLiteral(ns, upperBoundary, '' + filter.upperBoundary);
} }
/** /**
@@ -1085,41 +1250,61 @@ function writeIsBetweenFilter(node, filter, objectStack) {
* @param {Array<*>} objectStack Node stack. * @param {Array<*>} objectStack Node stack.
*/ */
function writeIsLikeFilter(node, filter, objectStack) { function writeIsLikeFilter(node, filter, objectStack) {
const parent = /** @type {Object} */ (objectStack[objectStack.length - 1]);
const context = parent['context'];
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(node, filter.propertyName); writePropertyName(version, node, filter.propertyName);
writeOgcLiteral(node, '' + filter.pattern); writeOgcLiteral(ns, node, '' + filter.pattern);
} }
/** /**
* @param {string} ns Namespace.
* @param {string} tagName Tag name. * @param {string} tagName Tag name.
* @param {Node} node Node. * @param {Node} node Node.
* @param {string} value Value. * @param {string} value Value.
*/ */
function writeOgcExpression(tagName, node, value) { function writeOgcExpression(ns, tagName, node, value) {
const property = createElementNS(OGCNS, tagName); const property = createElementNS(ns, tagName);
writeStringTextNode(property, value); writeStringTextNode(property, value);
node.appendChild(property); node.appendChild(property);
} }
/** /**
* @param {string} version Version.
* @param {Node} node Node. * @param {Node} node Node.
* @param {string} value PropertyName value. * @param {string} value PropertyName value.
*/ */
function writeOgcPropertyName(node, value) { function writePropertyName(version, node, value) {
writeOgcExpression('PropertyName', node, value); if (version === '2.0.0') {
writeFesValueReference(FESNS[version], node, value);
} else {
writeOgcExpression(OGCNS[version], 'PropertyName', node, value);
}
} }
/** /**
* @param {string} ns Namespace.
* @param {Node} node Node. * @param {Node} node Node.
* @param {string} value PropertyName value. * @param {string} value PropertyName value.
*/ */
function writeOgcLiteral(node, value) { function writeFesValueReference(ns, node, value) {
writeOgcExpression('Literal', node, value); writeOgcExpression(ns, 'ValueReference', node, value);
}
/**
* @param {string} ns Namespace.
* @param {Node} node Node.
* @param {string} value PropertyName value.
*/
function writeOgcLiteral(ns, node, value) {
writeOgcExpression(ns, 'Literal', node, value);
} }
/** /**
@@ -1139,12 +1324,20 @@ function writeTimeInstant(node, time) {
* Encode filter as WFS `Filter` and return the Node. * Encode filter as WFS `Filter` and return the Node.
* *
* @param {import("./filter/Filter.js").default} filter Filter. * @param {import("./filter/Filter.js").default} filter Filter.
* @param {string} version Version.
* @return {Node} Result. * @return {Node} Result.
* @api * @api
*/ */
export function writeFilter(filter) { export function writeFilter(filter, version) {
const child = createElementNS(OGCNS, 'Filter'); const child = createElementNS(OGCNS[version], 'Filter');
writeFilterCondition(child, filter, []); const context = {
node: child,
};
assign(context, {
'version': version,
'filter': filter,
});
writeFilterCondition(child, filter, [context]);
return child; return child;
} }
@@ -1169,4 +1362,14 @@ function writeGetFeature(node, featureTypes, objectStack) {
); );
} }
function getFilterNS(version) {
let ns;
if (version === '2.0.0') {
ns = FESNS[version];
} else {
ns = OGCNS[version];
}
return ns;
}
export default WFS; export default WFS;
+21
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';
@@ -17,6 +18,7 @@ import LessThanOrEqualTo from './filter/LessThanOrEqualTo.js';
import Not from './filter/Not.js'; import Not from './filter/Not.js';
import NotEqualTo from './filter/NotEqualTo.js'; import NotEqualTo from './filter/NotEqualTo.js';
import Or from './filter/Or.js'; import Or from './filter/Or.js';
import ResourceId from './filter/ResourceId.js';
import Within from './filter/Within.js'; import Within from './filter/Within.js';
/** /**
@@ -99,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.
@@ -260,3 +277,7 @@ export function like(
export function during(propertyName, begin, end) { export function during(propertyName, begin, end) {
return new During(propertyName, begin, end); return new During(propertyName, begin, end);
} }
export function resourceId(rid) {
return new ResourceId(rid);
}
+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;
+25
View File
@@ -0,0 +1,25 @@
/**
* @module ol/format/filter/ResourceId
*/
import Filter from './Filter.js';
/**
* @classdesc
*
* @abstract
*/
class ResourceId extends Filter {
/**
* @param {!string} rid Resource ID.
*/
constructor(rid) {
super('ResourceId');
/**
* @type {!string}
*/
this.rid = rid;
}
}
export default ResourceId;
+216 -1
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,
@@ -27,6 +29,7 @@ import {
like as likeFilter, like as likeFilter,
not as notFilter, not as notFilter,
or as orFilter, or as orFilter,
resourceId as resourceIdFilter,
within as withinFilter, within as withinFilter,
} from '../../../../src/ol/format/filter.js'; } from '../../../../src/ol/format/filter.js';
import {parse} from '../../../../src/ol/xml.js'; import {parse} from '../../../../src/ol/xml.js';
@@ -1376,9 +1379,221 @@ describe('ol.format.WFS', function () {
andFilter( andFilter(
likeFilter('name', 'Mississippi*'), likeFilter('name', 'Mississippi*'),
equalToFilter('waterway', 'riverbank') equalToFilter('waterway', 'riverbank')
) ),
'1.1.0'
); );
expect(serialized).to.xmleql(parse(text)); expect(serialized).to.xmleql(parse(text));
}); });
}); });
describe('WFS 2.0.0', function () {
before(function (done) {
proj4.defs(
'http://www.opengis.net/def/crs/EPSG/0/26713',
'+proj=utm +zone=13 +ellps=clrk66 +datum=NAD27 +units=m +no_defs'
);
proj4.defs(
'urn:ogc:def:crs:EPSG::26713',
'+proj=utm +zone=13 +ellps=clrk66 +datum=NAD27 +units=m +no_defs'
);
register(proj4);
done();
});
after(function () {
delete proj4.defs['http://www.opengis.net/def/crs/EPSG/0/26713'];
delete proj4.defs['urn:ogc:def:crs:EPSG::26713'];
clearAllProjections();
addCommon();
});
it('can writeGetFeature query with simple resourceId filter', function () {
const getFeatureXml = `
<?xml version="1.0" encoding="UTF-8"?>
<!--
This example demonstrates a WFS 2.0 GetFeature POST request.
This filter selects a single feature with id "bugsites.3".
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:ResourceId rid="bugsites.3"/>
</fes:Filter>
</wfs:Query>
</wfs:GetFeature>
`.trim();
const wfs = new WFS({
version: '2.0.0',
});
const filter = resourceIdFilter('bugsites.3');
const serialized = wfs.writeGetFeature({
featureNS: 'http://www.openplans.org/spearfish',
featureTypes: ['bugsites'],
featurePrefix: 'sf',
filter,
});
expect(serialized).to.xmleql(parse(getFeatureXml));
});
it('can writeGetFeature query with negated disjoint spatial filter', function () {
const geometryXml = `
<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>
`.trim();
const getFeatureXml = `
<?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>
${geometryXml}
</fes:Disjoint>
</fes:Not>
</fes:Filter>
</wfs:Query>
</wfs:GetFeature>
`.trim();
const wfs = new WFS({
version: '2.0.0',
});
const geometryNode = parse(geometryXml);
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(getFeatureXml));
});
it('can parse basic GetFeature response', function () {
const response = `
<?xml version="1.0" encoding="UTF-8"?>
<wfs:FeatureCollection xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sf="http://www.openplans.org/spearfish"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" numberMatched="1" numberReturned="1" timeStamp="2020-08-11T12:18:35.474Z" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://localhost:8080/geoserver/schemas/wfs/2.0/wfs.xsd http://www.openplans.org/spearfish http://localhost:8080/geoserver/wfs?service=WFS&amp;version=2.0.0&amp;request=DescribeFeatureType&amp;typeName=sf%3Abugsites http://www.opengis.net/gml/3.2 http://localhost:8080/geoserver/schemas/gml/3.2.1/gml.xsd">
<wfs:member>
<sf:bugsites gml:id="bugsites.3">
<sf:the_geom>
<gml:Point srsName="urn:ogc:def:crs:EPSG::26713" srsDimension="2" gml:id="bugsites.3.the_geom">
<gml:pos>590529 4914625</gml:pos>
</gml:Point>
</sf:the_geom>
<sf:cat>3</sf:cat>
<sf:str1>Beetle site</sf:str1>
</sf:bugsites>
</wfs:member>
</wfs:FeatureCollection>
`.trim();
const wfs = new WFS({
version: '2.0.0',
});
const features = wfs.readFeatures(parse(response));
expect(features.length).to.be(1);
expect(features[0]).to.be.an(Feature);
});
describe('when writing out a Transaction request', function () {
it('creates a handle', function () {
const text =
'<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs/2.0" ' +
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
'service="WFS" version="2.0.0" handle="handle_t" ' +
'xsi:schemaLocation="http://www.opengis.net/wfs/2.0 ' +
'http://schemas.opengis.net/wfs/2.0/wfs.xsd"/>';
const serialized = new WFS({
version: '2.0.0',
}).writeTransaction(null, null, null, {
handle: 'handle_t',
});
expect(serialized).to.xmleql(parse(text));
});
});
describe('when writing out a Transaction request', function () {
it('creates the correct srsName', function () {
const text = `
<wfs:Transaction xmlns:wfs="http://www.opengis.net/wfs/2.0" service="WFS" version="2.0.0" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfs:Insert>
<feature:FAULTS xmlns:feature="http://foo">
<feature:the_geom>
<gml:MultiCurve xmlns:gml="http://www.opengis.net/gml/3.2" srsName="EPSG:900913">
<gml:curveMember>
<gml:LineString srsName="EPSG:900913">
<gml:posList srsDimension="2">-5178372.1885436 1992365.7775042 -4434792.7774889 1601008.1927386 -4043435.1927233 2148908.8114105</gml:posList>
</gml:LineString>
</gml:curveMember>
</gml:MultiCurve>
</feature:the_geom>
<feature:TYPE>xyz</feature:TYPE>
</feature:FAULTS>
</wfs:Insert>
</wfs:Transaction>
`.trim();
const format = new WFS({
version: '2.0.0',
});
const insertFeature = new Feature({
the_geom: new MultiLineString([
[
[-5178372.1885436, 1992365.7775042],
[-4434792.7774889, 1601008.1927386],
[-4043435.1927233, 2148908.8114105],
],
]),
TYPE: 'xyz',
});
insertFeature.setGeometryName('the_geom');
const inserts = [insertFeature];
const serialized = format.writeTransaction(inserts, null, null, {
featureNS: 'http://foo',
featureType: 'FAULTS',
featurePrefix: 'feature',
gmlOptions: {multiCurve: true, srsName: 'EPSG:900913'},
});
expect(serialized).to.xmleql(parse(text));
});
});
});
}); });