Merge pull request #11740 from ger-benjamin/wfs_bbox_per_feature_type_11187
Wfs bbox per feature type
This commit is contained in:
@@ -47,7 +47,7 @@ The default `geometryFunction` can only handle `ol/geom/Point` geometries.
|
||||
|
||||
### 11
|
||||
|
||||
`options.featureTypes` should be an Array.
|
||||
`options.featureTypes` must be an Array.
|
||||
|
||||
### 12
|
||||
|
||||
@@ -252,4 +252,4 @@ A layer can only be added to the map once. Use either `layer.setMap()` or `map.a
|
||||
|
||||
### 68
|
||||
|
||||
Data from this source can only be rendered if it has a projection compatible with the view projection.
|
||||
Data from this source can only be rendered if it has a projection compatible with the view projection.
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
pushParseAndPop,
|
||||
pushSerializeAndPop,
|
||||
} 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 {assign} from '../obj.js';
|
||||
import {get as getProjection} from '../proj.js';
|
||||
@@ -129,7 +129,9 @@ const TRANSACTION_SERIALIZERS = {
|
||||
* @typedef {Object} WriteGetFeatureOptions
|
||||
* @property {string} featureNS The namespace URI used for features.
|
||||
* @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
|
||||
* geometries when this is not provided.
|
||||
* @property {string} [handle] Handle.
|
||||
@@ -143,13 +145,21 @@ const TRANSACTION_SERIALIZERS = {
|
||||
* @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
|
||||
* 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
|
||||
* {@link module:ol/format/Filter} for more information.
|
||||
* @property {string} [resultType] Indicates what response should be returned,
|
||||
* 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
|
||||
* @property {string} featureNS The namespace URI used for features.
|
||||
@@ -471,44 +481,26 @@ class WFS extends XMLFeature {
|
||||
const node = createElementNS(WFSNS[this.version_], 'GetFeature');
|
||||
node.setAttribute('service', 'WFS');
|
||||
node.setAttribute('version', this.version_);
|
||||
let filter;
|
||||
if (options) {
|
||||
if (options.handle) {
|
||||
node.setAttribute('handle', options.handle);
|
||||
}
|
||||
if (options.outputFormat) {
|
||||
node.setAttribute('outputFormat', options.outputFormat);
|
||||
}
|
||||
if (options.maxFeatures !== undefined) {
|
||||
node.setAttribute('maxFeatures', String(options.maxFeatures));
|
||||
}
|
||||
if (options.resultType) {
|
||||
node.setAttribute('resultType', options.resultType);
|
||||
}
|
||||
if (options.startIndex !== undefined) {
|
||||
node.setAttribute('startIndex', String(options.startIndex));
|
||||
}
|
||||
if (options.count !== undefined) {
|
||||
node.setAttribute('count', String(options.count));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (options.handle) {
|
||||
node.setAttribute('handle', options.handle);
|
||||
}
|
||||
if (options.outputFormat) {
|
||||
node.setAttribute('outputFormat', options.outputFormat);
|
||||
}
|
||||
if (options.maxFeatures !== undefined) {
|
||||
node.setAttribute('maxFeatures', String(options.maxFeatures));
|
||||
}
|
||||
if (options.resultType) {
|
||||
node.setAttribute('resultType', options.resultType);
|
||||
}
|
||||
if (options.startIndex !== undefined) {
|
||||
node.setAttribute('startIndex', String(options.startIndex));
|
||||
}
|
||||
if (options.count !== undefined) {
|
||||
node.setAttribute('count', String(options.count));
|
||||
}
|
||||
if (options.viewParams !== undefined) {
|
||||
node.setAttribute('viewParams', options.viewParams);
|
||||
}
|
||||
node.setAttributeNS(
|
||||
XML_SCHEMA_INSTANCE_URI,
|
||||
@@ -524,20 +516,67 @@ class WFS extends XMLFeature {
|
||||
'srsName': options.srsName,
|
||||
'featureNS': options.featureNS ? options.featureNS : this.featureNS_,
|
||||
'featurePrefix': options.featurePrefix,
|
||||
'geometryName': options.geometryName,
|
||||
'filter': filter,
|
||||
'propertyNames': options.propertyNames ? options.propertyNames : [],
|
||||
});
|
||||
|
||||
assert(Array.isArray(options.featureTypes), 11); // `options.featureTypes` should be an Array
|
||||
writeGetFeature(
|
||||
node,
|
||||
/** @type {!Array<string>} */ (options.featureTypes),
|
||||
[context]
|
||||
);
|
||||
assert(Array.isArray(options.featureTypes), 11); // `options.featureTypes` must be an Array
|
||||
if (typeof options.featureTypes[0] === 'string') {
|
||||
let filter = options.filter;
|
||||
if (options.bbox) {
|
||||
assert(options.geometryName, 12); // `options.geometryName` must also be provided when `options.bbox` is set
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -316,6 +316,59 @@ describe('ol.format.WFS', function () {
|
||||
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 () {
|
||||
const text =
|
||||
'<wfs:Query xmlns:wfs="http://www.opengis.net/wfs" ' +
|
||||
|
||||
Reference in New Issue
Block a user