WFS

Introduction


Le protocole Web Feature Server initié par l'Open Géospatial Consortium a pour objectif de fournir, à partir d'une simple requête, un moyen d'accéder directement aux données géographiques autorisant ainsi la manipulation de ces dernières (modification, suppression, ajout...).

Un peu d'histoire


La première version (1.0.0) du protocole WFS date de Mai 2002, celle-ci reste la plus couramment utilisée dans la plupart des serveurs cartographiques. Il faut néanmoins préciser que depuis Mai 2005, une nouvelle version (1.1.0) est disponible.

Comment fonctionne le protocole WFS


Le protocole WFS permet au moyen d'une simple requête d'interroger directement les données d'un serveur cartographique. Au contraire d'un serveur WMS qui ne retourne qu'une image, le protocole WFS renvoie un fichier XML contenant les coordonnées de l'objet ainsi que ses informations attributaires. Ce qui permet ensuite de les afficher les données sous forme vectorielle .

Une requête de type WFS s'écrit de la manière suivante :

http ://url_To_Mon_Server_Carto?service=WFS
                                &version=1.0.0
                                &request=GetCapabilities

Ce qui donne schématiquement :

wfs.png

Quelles sont les requêtes possibles ?


Le document décrivant la norme WFS spécifie cinq types de requêtes :

  • GetCapabilities : Description des capacités du serveur WFS. Il indiquera les types de données ainsi que les opérations supportées sur chacune d'entre elles.
  • DescribeFeatureType : Description de la structure de la donnée.
  • GetFeature : Récupère un (ou les) objet géographique de la couche. Il est également possible de spécifier l'objet désiré au travers de requête attributaire ou spatiale.
  • GetGmlObject : A web feature service may be able to service a request to retrieve element instances by traversing XLinks that refer to their XML IDs. In addition, the client should be able to specify whether nested XLinks embedded in returned element data should also be retrieved.
  • Transaction : Support des requêtes transactionnelles. Une transaction peut être composée d'un ensemble d'opérations sur les objets (création, modification, mise à jour, suppression).
  • LockFeature : Possibilité de bloquer l'accès à un objet géographique durant une transaction par la pose d'un verrou.

En se basant sur les opérations ci-dessus, il est possible de classer les serveurs WFS en 3 catégories :

  • Basic WFS : Celui-ci doit, au moins, implémenter les opérations suivantes : GetCapabilities, DescribeFeatureType and GetFeature operations.
  • XLink WFS : Celui-ci doit, en plus des opérations d'un Serveur WFS basique, supporter l'opération GetGmlObject
  • WFS Transactionnel : Celui-ci doit, en plus des opérations d'un Serveur WFS basique, apporter un support transactionnel. Même si cela n'est pas obligatoire, il pourra également être capable de réaliser des opérations de type GetGmlObject et/ou LockFeature opérations

Prenons le cas de MapServer. Celui-ci intègre la norme WFS mais seulement d'un point de vue basic. Face à ce manque de nombreux projets ont vu le jour à l'exemple de FeatureServer

GetCapabilities

Une requête de type GetCapabilities renvoie un fichier XML contenant ensemble d'informations décrivant le service et ses capacités.. Prenons un exemple concret et interrogeons le serveur de géolittoral :

http://geolittoral.application.equipement.gouv.fr/wfs/metropole?service=WFS
&version=1.0.0&request=GetCapabilities

Ce fichier XMl est construit de la manière suivante :

<WFS_Capabilities version="1.0.0" xsi:schemaLocation="http://www.opengis.net/.../WFS-capabilities.xsd">
  <Service>...</Service>
  <Capability>...</Capability>
  <FeatureTypeList>...</FeatureTypeList>
  <ogc:FilterCapabilities>...</ogc:FilterCapabilities>
</WFS_Capabilities>

La partie décrivant les capacités du serveur WFS se situe entre les deux balises Capability :

<Capability>
  <Request>
    <GetCapabilities>
      <DCPType>
        <HTTP>
          <Get onlineResource="http://geolittoral.application.equipement.gouv.fr/.../map/metropole.www.map&"/>
        </HTTP>
      </DCPType>
      <DCPType>
        <HTTP>
          <Post onlineResource="http://geolittoral.application.equipement.gouv.fr/.../map/metropole.www.map&"/>
        </HTTP>
      </DCPType>
    </GetCapabilities>
    <DescribeFeatureType>...</DescribeFeatureType>
    <GetFeature>...</GetFeature>
    <Transaction>...</Transaction>
    <LockFeature>...</LockFeature>
    <GetFeatureWithLock>...</GetFeatureWithLock>
  </Request>
</Capability>

Vient ensuite la description des données disponibles. Ce bloc débute par les opérations qu'il est possible de réaliser (query en simple WFS et les autres en WFS-T) puis sont ensuite entre chaque bloc FeatureType sont présentées toutes les données disponibles :

<FeatureTypeList>
  <Operations>
    <Query/>
    <Insert/>
    <Update/>
    <Delete/>
    <Lock/>
  </Operations>
  <FeatureType>
    <Name>Sentiers_littoraux</Name>
    <Title>Sentiers littoraux</Title>
    <SRS>EPSG:27582</SRS>
    <LatLongBoundingBox minx="-5.5002" miny="43.3831" maxx="2.54697" maxy="51.0914"/>
  </FeatureType>
  <FeatureType>...</FeatureType>
  <FeatureType>...</FeatureType>
</FeatureTypeList>

Enfin, le dernier bloc nommé Filter_Capabilities présente la liste des opérateurs spatiaux ou attributaires qu'il est possible d'utiliser lorsque l'on souhaite pouvoir filtrer les résultats envoyés par le serveur. C'est un peu le SQL du WFS.

<ogc:Filter_Capabilities>
 <ogc:Spatial_Capabilities>
  <ogc:Spatial_Operators>
   <ogc:Equals/>
   <ogc:Disjoint/>
   <ogc:Touches/>
   <ogc:Within/>
   <ogc:Overlaps/>
   <ogc:Crosses/>
   <ogc:Intersect/>
   <ogc:Contains/>
   <ogc:DWithin/>
   <ogc:BBOX/>
  </ogc:Spatial_Operators>
 </ogc:Spatial_Capabilities>
 <ogc:Scalar_Capabilities>
  <ogc:Logical_Operators/>
  <ogc:Comparison_Operators>... <ogc:Simple_Comparisons/>
  <ogc:Like/>
  <ogc:Between/>
  </ogc:Comparison_Operators>
  </ogc:Scalar_Capabilities>
</ogc:Filter_Capabilities>

DescribeFeatureType

En nous basant sur la requête GetCapabilities, nous savons donc que le serveur que nous interrogeons possède une couche nommée "Sentiers_littoraux" (présente dans le bloc FeatureTypeList). Nous souhaiterions maintenant avoir plus de détails sur cette donnée
et en particulier sur sa structure. C'est là qu'intervient la requête de type DescribeFeatureType qui retourne un fichier XML où sont décrit entre les balises sequence chaque champs ainsi que son type.

<schema targetNamespace="http://www.ttt.org/myns" elementFormDefault="qualified" version="0.1">
  <import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengeospatial.net/gml/2.1.2/feature.xsd"/>
  <element name="Pays_europeTyp" type="myns:Pays_europeType" substitutionGroup="gml:_Feature"/>
    <complexType name="Pays_europeType">
      <complexContent>
        <extension base="gml:AbstractFeatureType">
          <sequence>
           <element name="msGeometry" type="gml:GeometryPropertyType" minOccurs="0" maxOccurs="1"/>
           <element name="NOM_PAYS" type="string"/>
        </sequence>
      </extension>
    </complexContent>
  </complexType>
</schema>

Nous sommes donc en mesure de connaitre les données présentes sur le serveur ainsi que leur description. il ne nous reste pus maintenant qu'à les afficher.

GetFeature

Pour accéder directement à la donnée il nous suffit de faire une requête de type GetFeature (comparable à GetMap pour le WMS) :

http://geolittoral.application.equipement.gouv.fr/wfs/metropole?service=WFS
&version=1.0.0&request=GetFeature&typeName=Pays_europe

Le résultat retourné est un fichier XML comprenant l'ensemble des données de la couche décrite au format Geographic Markup Language. Ce format également défini par l'OGC permet de décrire les objets géographiques, les systèmes de projection, la géométrie, les attributs...

<wfs:FeatureCollection xsi:schemaLocation="http://www.opengis.net/...&TYPENAME=Pays_europe&OUTPUTFORMAT=XMLSCHEMA">
<gml:boundedBy>
  <gml:Box srsName="EPSG:27582">
    <gml:coordinates>
       -1502547.500610,258118.872066 4674644.853092,6957954.142914
    </gml:coordinates>
  </gml:Box>
</gml:boundedBy>
<gml:featureMember>
  <myns:Pays_europe>
  <gml:boundedBy>
    <gml:Box srsName="EPSG:27582">
      <gml:coordinates>
        1437800.705550,2538332.227876 4394654.940187,6957954.142914
      </gml:coordinates>
    </gml:Box>
  </gml:boundedBy>
  <myns:msGeometry>
  <gml:MultiPolygon srsName="EPSG:27582">
    <gml:polygonMember>
      <gml:Polygon>
        <gml:outerBoundaryIs>
          <gml:LinearRing>
            <gml:coordinates>
              1830083.533234,5094379.939476 ... 2489370.647309,5669638.787560
            </gml:coordinates>
          </gml:LinearRing>
        </gml:outerBoundaryIs>
      </gml:Polygon>
    </gml:polygonMember>
    <gml:polygonMember>
      <gml:Polygon>
        <gml:outerBoundaryIs>
          <gml:LinearRing>
            <gml:coordinates>
              1795555.692097,6740983.968326 ... 3240586.069767,2637892.128872
            </gml:coordinates>
          </gml:LinearRing>
        </gml:outerBoundaryIs>
      </gml:Polygon>
    </gml:polygonMember>
    <gml:polygonMember>
      <gml:Polygon>
        <gml:outerBoundaryIs>
         <gml:LinearRing>
           <gml:coordinates>
              2030152.510330,3964544.789012 ... 2030152.510330,3964544.789012 
           </gml:coordinates>
         </gml:LinearRing>
       </gml:outerBoundaryIs>
     </gml:Polygon>
   </gml:polygonMember>
  </gml:MultiPolygon>
  </myns:msGeometry>
<myns:NOM_PAYS>Russie</myns:NOM_PAYS>
</myns:Pays_europe>
</gml:featureMember>
 <gml:featureMember>...</gml:featureMember>
 <gml:featureMember>...</gml:featureMember>
 ...
</wfs:FeatureCollection>

Bien évidemment, je n'ai pas affiché l'ensemble des pays d'Europe tout comme j'ai volontairement tronqué le nombre important de lat/long contenu dans la balise gml:coordinates.

L'exemple ci-dessus présente un cas intéressant, celui de la Russie. En effet, si vous regardez attentivement, vous verrez plusieurs balises gml:coordinates pour un même objet. En effet, celles-ci sont inscrites dans une balise gml:MultiPolygon pouvant donc contenir plusieurs géométries. C'est le cas par exemple pour un pays ayant des îles et dont la géométrie ne peut se résumer qu'à une seule entité.

Faire transiter par votre navigateur une grande quantité de données est toujours pénalisant. Lorsque vous avez lancé la requête précédente, vous avez pu remarquer le temps qu'il a fallu pour retourner l'ensemble des informations. Il faut donc trouver un moyen pour réduire la quantité de données retournée et donc la taille du fichier GML. C'est là qu'intervient la notion de filtre.

Filtre WFS


Comme son nom l'indique un filtre sert à réduire les informations retournées en fonction de critères spécifiques (similaire au SLD pour le WMS). Ces critères sont basés sur des opérateurs spatiaux (Equals, Disjoint, Touches etc), logiques et/ou comparatifs (PropertyIsBetween, PropertyIsLessThan etc).

Néanmoins, en fonction du serveur utilisé, certains filtres ne seront pas forcément implémentés. Pour en connaitre la liste, il suffit de consulter le bloc Filter_Capabilities renvoyé par une requête de type GetCapabalities.

Essayons maintenant de filtrer notre requête en utilisant comme attribut NOM_PAYS, comme valeur Russie et comme filtre IsLike. Ce qui donne :

http://geolittoral.application.equipement.gouv.fr/wfs/metropole?service=WFS
&version=1.0.0&request=GetFeature&typeName=Pays_europe&filter=
<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc" >
<ogc:PropertyIsLike><ogc:PropertyName>NOM_PAYS</ogc:PropertyName>
<ogc:Literal>Russie</ogc:Literal></ogc:PropertyIsLike></ogc:Filter>

Malheureusement, à moins qu'il ai modifié leur configuration, la seule chose que vous devriez voir apparaitre est un joli message d'erreur vous indiquant : Invalid or Unsupported FILTER in GetFeature. Ne cherchez l'erreur, elle ne vient pas de vous, mais du serveur utilisé par géolittoral (MapServer). En effet, pour qu'une requête de ce type soit acceptée, il faut que le filtre soit implicitement déclaré dans le mapfile. Mais là n'est aps le but de ce cours, si vous souhaitez avoir plus d'information, je vous conseille de lire le howto filterEncoding de MapServer.

Reprenons, donc le même exemple mais sur un serveur cartographique différent. Nous utiliserons pour cela celui de dmsolutions qui possède une couche de données dont le nom est popplace. Notre filtre portera sur tous les valeurs de poplace qui commencent par la lettre D.

http://www2.dmsolutions.ca/cgi-bin/mswfs_filter?
&VERSION=1.0.0&SERVICE=WFS&REQUEST=GetFeature&TYPENAME=popplace
&Filter=<Filter><PropertyIsLike wildcard='*' singleChar='.' escape='!'>
<PropertyName>NAME</PropertyName><Literal>D*</Literal></PropertyIsLike></Filter>

Le serveur renvoie alors deux résultats : Digby et Dartmouth.

Conclusion


Il est important de bien comprendre l'utilité d'une couche WFS. Si vous souhaitez simplement affiché vos couches de données, surtout si elles sont nombreuses, passer par du WMS sera alors bien suffisant et surtout plus adapté. Mais si par contre vous souhaitez accéder aux données elles-mêmes, pouvoir changer leur style à la volée ou les modifier alors le WFS est indispensable. Néanmoins, il faut l'utiliser avec précaution car du fait du nombre important d'informations qui transitent, il entraine un temps de chargement important.