Merge pull request #11740 from ger-benjamin/wfs_bbox_per_feature_type_11187

Wfs bbox per feature type
This commit is contained in:
Andreas Hocevar
2020-11-16 13:58:50 +01:00
committed by GitHub
3 changed files with 144 additions and 52 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ The default `geometryFunction` can only handle `ol/geom/Point` geometries.
### 11 ### 11
`options.featureTypes` should be an Array. `options.featureTypes` must be an Array.
### 12 ### 12
+89 -50
View File
@@ -19,7 +19,7 @@ import {
pushParseAndPop, pushParseAndPop,
pushSerializeAndPop, pushSerializeAndPop,
} from '../xml.js'; } from '../xml.js';
import {and as andFilter, bbox as bboxFilter} from './filter.js'; import {and as andFilterFn, bbox as bboxFilterFn} from './filter.js';
import {assert} from '../asserts.js'; import {assert} from '../asserts.js';
import {assign} from '../obj.js'; import {assign} from '../obj.js';
import {get as getProjection} from '../proj.js'; import {get as getProjection} from '../proj.js';
@@ -129,7 +129,9 @@ const TRANSACTION_SERIALIZERS = {
* @typedef {Object} WriteGetFeatureOptions * @typedef {Object} WriteGetFeatureOptions
* @property {string} featureNS The namespace URI used for features. * @property {string} featureNS The namespace URI used for features.
* @property {string} featurePrefix The prefix for the feature namespace. * @property {string} featurePrefix The prefix for the feature namespace.
* @property {Array<string>} featureTypes The feature type names. * @property {Array<string|FeatureType>} featureTypes The feature type names or FeatureType objects to
* define a unique bbox filter per feature type name (in this case, options `bbox` and `geometryName` are
* ignored.).
* @property {string} [srsName] SRS name. No srsName attribute will be set on * @property {string} [srsName] SRS name. No srsName attribute will be set on
* geometries when this is not provided. * geometries when this is not provided.
* @property {string} [handle] Handle. * @property {string} [handle] Handle.
@@ -143,13 +145,21 @@ const TRANSACTION_SERIALIZERS = {
* @property {number} [count] Number of features to retrieve when paging. This is a * @property {number} [count] Number of features to retrieve when paging. This is a
* WFS 2.0 feature backported to WFS 1.1.0 by some Web Feature Services. Please note that some * WFS 2.0 feature backported to WFS 1.1.0 by some Web Feature Services. Please note that some
* Web Feature Services have repurposed `maxfeatures` instead. * Web Feature Services have repurposed `maxfeatures` instead.
* @property {import("../extent.js").Extent} [bbox] Extent to use for the BBOX filter. * @property {import("../extent.js").Extent} [bbox] Extent to use for the BBOX filter. The `geometryName`
* option must be set.
* @property {import("./filter/Filter.js").default} [filter] Filter condition. See * @property {import("./filter/Filter.js").default} [filter] Filter condition. See
* {@link module:ol/format/Filter} for more information. * {@link module:ol/format/Filter} for more information.
* @property {string} [resultType] Indicates what response should be returned, * @property {string} [resultType] Indicates what response should be returned,
* E.g. `hits` only includes the `numberOfFeatures` attribute in the response and no features. * E.g. `hits` only includes the `numberOfFeatures` attribute in the response and no features.
*/ */
/**
* @typedef {Object} FeatureType
* @property {!string} name The feature type name.
* @property {!import("../extent.js").Extent} bbox Extent to use for the BBOX filter.
* @property {!string} geometryName Geometry name to use in the BBOX filter.
*/
/** /**
* @typedef {Object} WriteTransactionOptions * @typedef {Object} WriteTransactionOptions
* @property {string} featureNS The namespace URI used for features. * @property {string} featureNS The namespace URI used for features.
@@ -471,44 +481,26 @@ class WFS extends XMLFeature {
const node = createElementNS(WFSNS[this.version_], 'GetFeature'); const node = createElementNS(WFSNS[this.version_], 'GetFeature');
node.setAttribute('service', 'WFS'); node.setAttribute('service', 'WFS');
node.setAttribute('version', this.version_); node.setAttribute('version', this.version_);
let filter; if (options.handle) {
if (options) { node.setAttribute('handle', options.handle);
if (options.handle) { }
node.setAttribute('handle', options.handle); if (options.outputFormat) {
} node.setAttribute('outputFormat', options.outputFormat);
if (options.outputFormat) { }
node.setAttribute('outputFormat', options.outputFormat); if (options.maxFeatures !== undefined) {
} node.setAttribute('maxFeatures', String(options.maxFeatures));
if (options.maxFeatures !== undefined) { }
node.setAttribute('maxFeatures', String(options.maxFeatures)); if (options.resultType) {
} node.setAttribute('resultType', options.resultType);
if (options.resultType) { }
node.setAttribute('resultType', options.resultType); if (options.startIndex !== undefined) {
} node.setAttribute('startIndex', String(options.startIndex));
if (options.startIndex !== undefined) { }
node.setAttribute('startIndex', String(options.startIndex)); if (options.count !== undefined) {
} node.setAttribute('count', String(options.count));
if (options.count !== undefined) { }
node.setAttribute('count', String(options.count)); if (options.viewParams !== undefined) {
} node.setAttribute('viewParams', options.viewParams);
if (options.viewParams !== undefined) {
node.setAttribute('viewParams', options.viewParams);
}
filter = options.filter;
if (options.bbox) {
assert(options.geometryName, 12); // `options.geometryName` must also be provided when `options.bbox` is set
const bbox = bboxFilter(
/** @type {string} */ (options.geometryName),
options.bbox,
options.srsName
);
if (filter) {
// if bbox and filter are both set, combine the two into a single filter
filter = andFilter(filter, bbox);
} else {
filter = bbox;
}
}
} }
node.setAttributeNS( node.setAttributeNS(
XML_SCHEMA_INSTANCE_URI, XML_SCHEMA_INSTANCE_URI,
@@ -524,20 +516,67 @@ class WFS extends XMLFeature {
'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,
'geometryName': options.geometryName,
'filter': filter,
'propertyNames': options.propertyNames ? options.propertyNames : [], 'propertyNames': options.propertyNames ? options.propertyNames : [],
}); });
assert(Array.isArray(options.featureTypes), 11); // `options.featureTypes` must be an Array
assert(Array.isArray(options.featureTypes), 11); // `options.featureTypes` should be an Array if (typeof options.featureTypes[0] === 'string') {
writeGetFeature( let filter = options.filter;
node, if (options.bbox) {
/** @type {!Array<string>} */ (options.featureTypes), assert(options.geometryName, 12); // `options.geometryName` must also be provided when `options.bbox` is set
[context] filter = this.combineBboxAndFilter(
); options.geometryName,
options.bbox,
options.srsName,
filter
);
}
assign(context, {
'geometryName': options.geometryName,
'filter': filter,
});
writeGetFeature(
node,
/** @type {!Array<string>} */ (options.featureTypes),
[context]
);
} else {
// Write one query node per element in featuresType.
options.featureTypes.forEach((/** @type {FeatureType} */ featureType) => {
const completeFilter = this.combineBboxAndFilter(
featureType.geometryName,
featureType.bbox,
options.srsName,
options.filter
);
assign(context, {
'geometryName': featureType.geometryName,
'filter': completeFilter,
});
writeGetFeature(node, [featureType.name], [context]);
});
}
return node; return node;
} }
/**
* Create a bbox filter and combine it with another optional filter.
*
* @param {!string} geometryName Geometry name to use.
* @param {!import("../extent.js").Extent} extent Extent.
* @param {string=} opt_srsName SRS name. No srsName attribute will be
* set on geometries when this is not provided.
* @param {import("./filter/Filter.js").default=} opt_filter Filter condition.
* @return {import("./filter/Filter.js").default} The filter.
*/
combineBboxAndFilter(geometryName, extent, opt_srsName, opt_filter) {
const bboxFilter = bboxFilterFn(geometryName, extent, opt_srsName);
if (opt_filter) {
// if bbox and filter are both set, combine the two into a single filter
return andFilterFn(opt_filter, bboxFilter);
}
return bboxFilter;
}
/** /**
* Encode format as WFS `Transaction` and return the Node. * Encode format as WFS `Transaction` and return the Node.
* *
+53
View File
@@ -316,6 +316,59 @@ describe('ol.format.WFS', function () {
expect(serialized.firstElementChild).to.xmleql(parse(text)); expect(serialized.firstElementChild).to.xmleql(parse(text));
}); });
it('creates one BBOX filter per feature type', function () {
const textQuery1 =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' +
' typeName="topp:states_1" srsName="urn:ogc:def:crs:EPSG::4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
' <ogc:BBOX>' +
' <ogc:PropertyName>the_geom_1</ogc:PropertyName>' +
' <gml:Envelope xmlns:gml="http://www.opengis.net/gml" ' +
' srsName="urn:ogc:def:crs:EPSG::4326">' +
' <gml:lowerCorner>1 2</gml:lowerCorner>' +
' <gml:upperCorner>3 4</gml:upperCorner>' +
' </gml:Envelope>' +
' </ogc:BBOX>' +
' </ogc:Filter>' +
'</wfs:Query>';
const textQuery2 =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' +
' typeName="topp:states_2" srsName="urn:ogc:def:crs:EPSG::4326" ' +
' xmlns:topp="http://www.openplans.org/topp">' +
' <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
' <ogc:BBOX>' +
' <ogc:PropertyName>the_geom_2</ogc:PropertyName>' +
' <gml:Envelope xmlns:gml="http://www.opengis.net/gml" ' +
' srsName="urn:ogc:def:crs:EPSG::4326">' +
' <gml:lowerCorner>5 6</gml:lowerCorner>' +
' <gml:upperCorner>7 8</gml:upperCorner>' +
' </gml:Envelope>' +
' </ogc:BBOX>' +
' </ogc:Filter>' +
'</wfs:Query>';
const serialized = new WFS().writeGetFeature({
srsName: 'urn:ogc:def:crs:EPSG::4326',
featureNS: 'http://www.openplans.org/topp',
featurePrefix: 'topp',
featureTypes: [
{
name: 'states_1',
geometryName: 'the_geom_1',
bbox: [1, 2, 3, 4],
},
{
name: 'states_2',
geometryName: 'the_geom_2',
bbox: [5, 6, 7, 8],
},
],
});
expect(serialized.children.length).to.equal(2);
expect(serialized.firstElementChild).to.xmleql(parse(textQuery1));
expect(serialized.lastElementChild).to.xmleql(parse(textQuery2));
});
it('creates a property filter', function () { it('creates a property filter', function () {
const text = const text =
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' + '<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' +