Add featureTypesBbox option on WFS writeGetFeature
Add a possibility to provide one specific bbox per feature type on WFS writeGetFeature. This option results to one query node per featureTypesBbox item. One query node, for one feature type, will have a specific bbox filter and every query node will share the same others filters (if a filter option is defined).
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.
|
||||
@@ -151,6 +153,13 @@ const TRANSACTION_SERIALIZERS = {
|
||||
* 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.
|
||||
@@ -472,7 +481,6 @@ class WFS extends XMLFeature {
|
||||
const node = createElementNS(WFSNS[this.version_], 'GetFeature');
|
||||
node.setAttribute('service', 'WFS');
|
||||
node.setAttribute('version', this.version_);
|
||||
let filter;
|
||||
if (options.handle) {
|
||||
node.setAttribute('handle', options.handle);
|
||||
}
|
||||
@@ -494,21 +502,6 @@ class WFS extends XMLFeature {
|
||||
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(
|
||||
XML_SCHEMA_INSTANCE_URI,
|
||||
'xsi:schemaLocation',
|
||||
@@ -523,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