MapServer currently supports the OGC WFS 1.0.0 and 1.1.0 protocols. The latest
published version of WFS 2.0, and it is desirable that MapServer implements
it. Particularly in the context of the European Inspire directive that
mandates WFS 2.0 as the base protocol for the INSPIRE Download Services.

There are a few changes and additions in the response document of a GetCapabilities
request w.r.t WFS 1.1 :

the <ows:OperationsMetadata> element can have child elements that advertize
which conformance classes of the specification are supported by the implementation.
In the case of MapServer, we report as supported : ImplementsBasicWFS,
KVPEncoding, XMLEncoding, ImplementsResultPaging. We also publish the
server side limit on feature count in “CountDefault”.

the Filter_Capabilities respects the syntax of Filter Encoding 2.0 capabilities.
It has a <fes:Conformance> element that advertizes which parts of FE 2.0
are supported by the implementation. In the case of MapServer, we report as
supported : ImplementsQuery, ImplementsAdHocQuery, ImplementsResourceId,
ImplementsMinStandardFilter, ImplementsStandardFilter, ImplementsMinSpatialFilter,
ImplementsMinTemporalFilter and ImplementsMinimumXPath

We also now support the SECTIONS parameter of the GetCapabilities that enable
the user to request only parts of the GetCapabilities document. The value of
this parameter is a (comma separated) list of values among : All,ServiceIdentification,
ServiceProvider,OperationsMetadata,FeatureTypeList,Filter_Capabilities

Discovered during CITE testing, we now always add the namespace prefix in the
<Name> element of a feature type (could/should also probably be done for WFS 1.1.0).
If no user namespace is specified, the default “ms:” prefix is used.

GML 3.2.1 defines a mandatory gml:id attribute on each geometry element :
as Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon.
The id will be generated as “feature_gml_id.serial_number” where
feature_gml_id is the gml:id on the feature element of the geometry, and
serial_number a number starting from 1 and incremented from each subgeometry
(points in a multipoint, etc.)

Regarding SRS, for WFS 2.0, it will always be reported with the “urn:ogc:def:crs:EPSG::”
syntax. This should likely also be done for WFS 1.1.0. The “wfs_return_srs_as_urn” can
be set to “true” or “false” to enable or disable this behaviour. It defaults to “true”
for WFS 2.0 and to “false” for older versions.

Contrary to the previous versions of WFS, WFS 2.0 in its XML Schema definition
does not import any GML schema, so it is possible to generate valid GetFeature responses
with other GML output formats. In particular, GML 2.1.1 and GML 3.1.1 that are
already handled for WFS 1.0 and 1.1 can be used for output of DescribeFeatureType,
GetFeature and GetPropertyValue.

The “PropertyName” element is replaced by a “ValueReference” element, with
equivalent semantic. Likewise, the WFS 1.1 <ogc:GmlObjectId gml:id=”XXXX”/>
operator is replaced by <fes:ResourceId rid=”XXXX”/>.

For the sake of compliance testing, rather dummy implementations of
PropertyIsNull and PropertyIsNil operators have been introduced, as MapServer
cannot generate nullable or nillable fields.

FE 2.0 MinTemporalFilter conformance class is supported with <fes:During> as
operator and <gml:TimePeriod> as operand.

Response paging was already supported in MapServer for WFS 1.1.0 as an extension.
This is now a standard (optional) feature of WFS 2.0.
Paging is triggered by the use of STARTINDEX (beginning at 0 for first index)
and COUNT (COUNT is the equivalent of WFS 1.1 MAXFEATURES) parameters.

The FeatureCollection element has the “numberReturned” and “numberMatched” mandatory
attributes.

numberReturned contains the number of features returned in the document (0 is
always eturned when RESULTTYPE=hits is specified).

numberMatched contains the number of features that matches the criterion of the
request.
If a “wfs_maxfeatures” metadata item is defined in the mapfile (or COUNT is specified
in the request), “unknown” will be returned if the number of matching features
reaches at least the server-side or client-side limit. This is to avoid performance
issues on the server. By defining “wfs_compute_number_matched” “true”, the
exact number of matching features will always be computed, but this can cause
performance issues.

When relevant, MapServer will fill the “next” and “previous” optional attributes
that contain URL to navigate among the response pages.

The request SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&TYPENAMES=province&COUNT=1&STARTINDEX=2
will generate the following response :

Stored queries are queries (<wfs:Query>) stored on the server, potentially with parameters
whose value can be assigned by the client during a GetFeature request.

At WEB.METADATA level, a “wfs_storedqueries” metadata item can be specified with a
comma separated list of stored queries ids.

Then for each stored query id, “wfs_{storedqueryid}_inlinedef” must be specified
with a valid <StoredQueryDescription> as value. As it is not always convenient to
specify a inline XML content, it is also possible to specify
“wfs_{storedqueryid}_filedef” whose value is a filename that contains
the <StoredQueryDescription> XML content.

A valid request is :
REQUEST=GetFeature&STOREDQUERY_ID=urn:ogc:def:query:OGC-WFS::GetFeatureById&ID=mylayer.3

The definition hardcoded in MapServer is :

<StoredQueryDescriptionid="urn:ogc:def:query:OGC-WFS::GetFeatureById"><Title>Get feature by identifier</Title><Abstract>Returns the single feature whose value is equal to the specified value of the ID argument</Abstract><Parametername="ID"xmlns:xs="http://www.w3.org/2001/XMLSchema"type="xs:string"/><QueryExpressionTextisPrivate="true"language="urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression"returnFeatureTypes=""><Queryxmlns:fes="http://www.opengis.net/fes/2.0"typeNames="?"><fes:Filter><fes:ResourceIdrid="${ID}"/></fes:Filter></Query></QueryExpressionText></StoredQueryDescription>

Comparing to a more standard stored query description, there are 2 oddities :
The value of the “returnFeatureTypes” attribute is replaced at runtime by
the name of the valid WFS layers of the mapfile (when issuing the response document to DescribeStoredQueries
request). And the value of the typeNames attribute is deduced at runtime from the value
of the passed ID parameter.

It is possible to override this hardcoded definition by a custom one (for example
to provide alternate values for other languages) by defining
“wfs_urn:ogc:def:query:OGC-WFS::GetFeatureById_inlinedef” or “wfs_urn:ogc:def:query:OGC-WFS::GetFeatureById_filedef”.
The value of <QueryExpressionText> attributes and child elements must however be
strictly identical to the hard-coded definition for correct execution.

A GetFeature using urn:ogc:def:query:OGC-WFS::GetFeatureById seems to be identical
to a GetFeature with a RESOURCEID parameter, but there is a difference. GetFeatureById
returns the feature directly as the response :

The DescribeStoredQueries operation accept an optional STOREDQUERY_ID parameter
that list the ids of stored queries. If not specified, all stored queries will
be returned with their full description.

MapServer will honour the value of the isPrivate attribute of <QueryExpressionText>.
If defined to “true”, the <Query> nodes will not be returned in the response to
a DescribeStoredQueries request.

<wfs:DescribeStoredQueriesResponsexmlns:wfs="http://www.opengis.net/wfs/2.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.opengis.net/wfs/2.0"xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd"><StoredQueryDescriptionid="urn:ogc:def:query:OGC-WFS::GetFeatureById"><Title>Get feature by identifier</Title><Abstract>Returns the single feature whose value is equal to the specified value of the ID argument</Abstract><Parameterxmlns:xs="http://www.w3.org/2001/XMLSchema"name="ID"type="xs:string"/><QueryExpressionTextxmlns:ms="http://mapserver.gis.umn.edu/mapserver"isPrivate="true"language="urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression"returnFeatureTypes="ms:province ms:point"/></StoredQueryDescription><StoredQueryDescriptionxmlns:xs="http://www.w3.org/2001/XMLSchema"xmlns:gml="http://www.opengis.net/gml/3.2"xmlns:fes="http://www.opengis.net/fes/2.0"id="myquery"><Title>query title</Title><Abstract>query abstract</Abstract><Parametername="longmin"type="xs:double"/><Parametername="latmin"type="xs:double"/><Parametername="longmax"type="xs:double"/><Parametername="latmax"type="xs:double"/><QueryExpressionTextxmlns:ms="http://mapserver.gis.umn.edu/mapserver"isPrivate="false"language="urn:ogc:def:queryLanguage:OGC-WFS::WFS_QueryExpression"returnFeatureTypes="ms:point"><Queryxmlns:fes="http://www.opengis.net/fes/2.0"typeNames="ms:point"><fes:Filter><fes:BBOX><fes:ValueReference>msGeometry</fes:ValueReference><gml:EnvelopesrsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>${latmin} ${longmin}</gml:lowerCorner><gml:upperCorner>${latmax} ${longmax}</gml:upperCorner></gml:Envelope></fes:BBOX></fes:Filter></Query></QueryExpressionText></StoredQueryDescription></wfs:DescribeStoredQueriesResponse>

Most parameters of GetFeature operation,
such as FILTER, RESOURCEID, BBOX, STOREDQUERY_ID or paging,
can also be used for a GetPropertyValue operation, so most of the logic of
msWFSGetFeature() has been refactored so that it can be also reused by msWFSGetPropertyValue().

msLayerSupportsSorting() must be updated if a new provider supports sorting.
msLayerSetSort() is called by the WFS code. And msLayerBuildSQLOrderBy() can
be called by providers that implement sorting as a “ORDER BY” SQL clause, which
is the case for PostGIS and OGR.

Tickets https://github.com/mapserver/mapserver/issues/3563 and
https://github.com/mapserver/mapserver/issues/3319 raise an issue about how
mandatory/optional GML items are handled. Up to now, all advertized properties
in the DescribeFeatureType response documents were mandatory (since minOccurs
was not specified). But when issuing a GetFeature with PROPERTYNAME, only
the specified properties were returned in the GetFeature response document, which
results in a document not compliant with the GML application schema.

The solution for this is to introduce a “gml_optional_items” element in the LAYER
metadata. By default,
now all items will be considered as optional (minOccurs=”0” in the GML application
schema). If “gml_optional_items” is specified, only the listed elements will be
optional and other included items will be mandatory. This change is the most backward
compatible solution for the current behaviour : only the output of DescribeFeatureType
will be changed (minOccurs=”0”), but GetFeature with PROPERTYNAME will still return
only the mentioned properties.
If there are more mandatory items, it might be more convenient to specify
“gml_mandatory_items”.

Default behaviour with this implementation :

"gml_optional_items""all"

All items optional except the ones mentioned :

"gml_mandatory_items""a_required_item,another_one"

All items mandatory :

"gml_mandatory_items""all"

All items mandatory except the ones mentioned :

"gml_optional_items""an_optional_item,another_one"

In addition to this, again for backward compatibility, when a GetFeature request
without explicit PROPERTYNAME is processed, we will return all items,
either optional or mandatory. This behaviour can now be amended by specifying
“gml_default_items” to specify which items (among the optional ones) must be
returned.

INSPIRE Technical Guidance Download Services v3.1 can be implemented with
Atom feeds (not supported by this RFC) and/or WFS 2.0. So we will implement
Inspire D.S. conformance classes 2 (Pre-defined WFS) and 3 (Direct WFS).
The mandatory conformance class “4: Quality of Service” must be addressed by other means than
the WFS 2.0 implementation itself.

Regarding mapfile configuration, the same metadata items as in “Provision of INSPIRE specific metadata”
of INSPIRE View Service must be provided (in the WFS or OWS namespaces).
In addition to them, for Download Services, the mandatory “wfs_inspire_dsid_code” metadata
item must be set with a Spatial Data Set Identifier (or a list of identifiers
separated by commas). The semantics attached to this parameter is described
at paragraph 4.1.3 of the technical guidance. If needed, the namespace linked to
the dataset identifier can be specified with “wfs_inspire_dsid_ns”.

Those metadata items are used to generate a <inspire_dls:ExtendedCapabilities> element
in the <ows:ExtendedCapabilities> element of the GetCapabilities response document.

INSPIRE Download Services also include support for several languages.
The definition of which languages are supported is done with the “wfs_languages” metadata
keyword similarly to View Services.
A “LANGUAGE=xxx” extra parameter in WFS KVP requests will then have
effect on the GetCapabilities response documents where the information under the
<ows:ServiceProvider> element as well as <Title>, <Abstract> and <ows:Keywords>
in the <FeatureTypeList> section will be translated into the request language, provided
that the translated version is provided in the mapfile. As an extension of
the recommandations of the technical guidance, the values of the DATA and CONNECTION
keywords can contain a “%language%” string that will be replaced by the requested
language.

"wfs_inspire_capabilities""embed""wfs_languages""eng,fre,ger"#first default, values according ISO 639-2/B"wfs_inspire_temporal_reference""2011-09-19"#date of last revision, value according YYYY-MM-DD"wfs_inspire_mpoc_name""mympocname"#point of contact"wfs_inspire_mpoc_email""mympoc@e.mail"#point of contact"wfs_inspire_metadatadate""2011-09-19""wfs_inspire_resourcelocator""http://myinspireresource"#URL for ResourceLocator"wfs_inspire_keyword""infoMapAccessService"#value according "classification of spatial data services""wfs_inspire_dsid_code""mycode,mycode2,mycode3""wfs_inspire_dsid_ns""http://mycode,,http://mycode3"

CMakeLists.txt : add mapwfs20.c, fix a few dependency issues
mapfile.c : refactoring of msLoadProjectionString(), initialization/free of
layer->sortBy
mapgml.c : GML 3.2.1 and WFS 2.0 GetFeature output specificities. Part of handling
of "gml_optional_items", "gml_mandatory_items" and "gml_default_items"
mapgml.h : move some #define into another include files
mapio.h/.c : add msIO_pushStdoutToBufferAndGetOldContext() and
msIO_restoreOldStdoutContext(), so that we can reuse without any change
the generation of Inspire View Services extended metadata in a libxml2 DOM document
maplayer.c : new sorting functions
mapogcfilter.h/.c : Filter Encoding 2.0 support, temporal filtering, simplification
of existing code
mapogcfiltercommon.c : temporal filtering
mapogcsld.c: adaptation for a renaming of a function called
mapogcsos.c: adaptations for a renaming of a function called and change of prototypes
mapogr.cpp: support for a filter string that combine both a SQL where clause that
can be passed to OGR, and a time filtering part that is processed on
MapServer side; support for sorting
mapows.c : fixes for a few memory leaks; wfs_cite_wfs2 hack; other misc minor changes;
add msOWSGetInspireSchemasLocation()
mapows.h : extend wfsParamsObj and gmlItemObj structures; other misc changes
mapowscommon.c : support for translation of ServiceIdentification and
ServiceProvider sections; support for validation of WFS 2.0
XML post requests
mapowscommon.h : #define for namespaces, URIs
mappostgis.c : support for sorting
mapproject.h/.c : hack for reprojection worldwide long/lat BBOX to projected SRS;
add msIsAxisInvertedProj() and msAxisSwapShape()
mapquery.c : use only_cache_result_count flag if "wfs_compute_number_matched" "true"
is specified; fix a bug in msQueryByRect when reprojection is involved;
mapserver.h : prototype and structure changes for sorting support; other misc changes
maptime.h/.c : const sanitization
maputil.c : add msMapSetLanguageSpecificConnection()
mapwcs11.c : adaptations for change of prototypes
mapwcs20.c : adaptations for change of prototypes
mapwfs.c : lots of changes !
mapwfs11.c : a few changes to make msWFSDumpLayer11() usable by WFS 2.0 too
mapwfs20.c : new file with the code that only applies to WFS 2.0 : GetCapabilities,
stored queries management.
mapwms.c : adaptations for change of prototypes

The only potential compatibility issue foreseen is related to the GetCapabilities
request. Without any explicit version mentioned in the request, the WFS
implementation will serve a GetCapabilities response document that conforms
to the latest version of WFS it implements (as recommended by OWS 1.1), that
is to say 2.0 with this new development. Which could cause confusion to
WFS clients not ready to deal with it (OWS 1.1 recommand that clients specify in
the ACCEPTVERSIONS parameter the list of versions they can deal with.)
The solution for this is to define the wfs_getcapabilities_version keyword in
the WEB.METADATA to “1.0” or “1.1”.

A very small subset of XPath 2.0 is supported (even if we shamelessly advertize
ImplementsMinimumXPath to make CITE tests happy). This is limited to property
names, “groupname/propertyname” syntax when using gml_group, as well as @gml:id for
the VALUEREFERENCE parameter of GetPropertyValue.

Locking WFS

Transactional WFS

Feature versions

Inheritance / schema-element() XPath function

SOAP

Namespaces: the NAMESPACE KVP parameter is ignored. namespace prefixes in
layer and property names are just removed.

The OGC Compliance and Interoperability Testing Initiative (CITE) provides
automatic tests to validate the implementation. As of today, the tests for WFS 2.0
are in beta : http://cite.opengeospatial.org/te2/

Note: one of the CITE test consists in sending a REQUEST=GetCapabilities GET request
without any SERVICE. As MapServer implements different OGC services in different
versions, it is impossible to serve a response that please all specifications
together. By default, MapServer sends an HTML error that doesn’t respect WFS 2.0.
In order to get CITE compliance, consequently, one needs to define a “wfs_cite_wfs2”
WEB metadata that will return the right exception.