Open WPS Platform

Jouer avec les blocs de construction - Créer des Services Web JavaScript

Introduction

Cette section illustre comment vous pouvez utiliser JavaScript côté serveur pour chaîner les services ensemble pour en construire de nouveaux. Vous allez créer un fournisseur de services ZOO utilisant les services que vous avez vu auparavant et le serveur WFS utilisant la ZOO-API. L’objectif final est d’interroger tous les POI inclus dans un tampon autour d’un objet et de les mettre en avant en utilisant un masque autour de ce tampon. La capture d’écran ci-dessous vous montre le résultat attendu:

../../_images/BufferMaskAndRequest_Level_151.png

Pour l’interface de routage, le résultat devrait ressembler à ceci:

../../_images/BufferMaskAndRequest_Routing_Level_15.png

Vous pouvez décomposer le résultat ci-dessus dans deux différents: le masque autour du tampon et les points inclus dans le tampon. Donc, vous allez créer deux services différents: l’un appelé BufferMask et l’autre appelée BufferRequest.

Mais avant d’implémenter tout service JavaScript, vous allez avoir un aperçu de la façon d’utiliser la ZOO API depuis votre installation ZOO-projet dans la section suivante.

Comme précédemment, vous créez en premier un nouveau répertoire pour stocker les fichiers de votre nouveau fournisseur de services:

mkdir -p ~/zoo-ws2013/jschains/cgi-env/

Aperçu de la ZOO-API

Le support JavaScript de la ZOO-API et du ZOO-Kernel vous rend capable d’exécuter des services implémentés en JavaScript côté serveur. Le JavaScript est un langage de programmation populaire, mais la plupart du temps utilisé côté client. Disons à partir d’un navigateur, mais ici c’est un peu différent.

Pour supporter le langage JavaScript, le ZOO-Kernel utilise l’API SpiderMonkey pour créer un environnement d’exécution javascript à partir duquel il va charger votre fichier JS puis extraire la fonction correspondante au service à exécuter à l’aide des paramètres pré-remplis. L’environnement d’exécution JavaScript créé par le ZOO-Kernel dépend de votre configuration. Si vous avez placé ZOO-api.js et ZOO-proj4js.js dans le même répertoire que votre ZOO-Kernel, cela signifie que votre environnement contient la ZOO-API et que Proj4js sera chargé avant votre service. Dans ce cas, vous pouvez accéder aux classes définies dans la ZOO-API JavaScript pour manipuler des données géographiques. Pour plus d’informations, merci de vous référer à la Documentation de la ZOO-API.

Même si elle peut être utile pour exécuter le JavaScript côté serveur, vous devriez vous rappeler que certaines fonctions de base JavaScript dont vous êtes familier n’existent pas ou ont un comportement différent. Par exemple, la simple fonction alert affichera des messages dans les journaux d’erreurs Apache plutôt que dans une fenêtre lorsqu’il est utilisé depuis un navigateur. La fonction alert peut être utilisée comme suit:

alert("My alert message");

Il n’y a pas de XMLHttpRequest disponible dans l’environnement JavaScript où votre service s’exécute. Heureusement, le ZOO-Kernel expose une fonction C au monde JavaScript nommée: JSRequest. Cette fonction vous rend capable depuis vos services JavaScript d’appeler d’autres services WPS (localement ou distants) ou d’autres types de services OGC tels que le WFS. Lorsque vous utilisez la ZOO-API, il est possible d’appeler des services en utilisant une instance ZOO.Process [1], pour parser les réponses WPS utilisant ZOO.Format.WPS (cf. ref).

Quant aux services de Python que vous avez déjà vu dans les sections précédentes, les fonctions correspondantes à un service doivent prendre trois arguments: conf, inputs et outputs [2]. Néanmoins, comme le ZOO-Kernel n’est pas en mesure d’accéder aux valeurs modifiées [3] par le code de service, plutôt que de retourner un entier comme en Python, ici vous devez retourner à la fois la valeur de nombre entier représentant le statut de votre service dans un objet JavaScript et les valeurs outputs résultantes comme un objet [4]. Vous pouvez voir dans ce qui suit un exemple de code de service JavaScript:

function SampleService(conf,inputs,outputs){
  var resultValue=someComputation(inputs);
  return
    {
        result: ZOO.SERVICE_SUCCEEDED,
        outputs: { "Result": { "mimeType": "application/json", "value": resultValue } }
    };
}

Avant de commencer à implémenter les services dont nous aurons besoin pour obtenir notre service BufferRequest final, commençons avec un plus simple.

Le service Mask

Dans cette section, vous apprendrez comment créer votre premier service JavaScript qui renvoie simplement un masque rectangulaire autour d’un objet sélectionné. Pour construire ce masque, vous utiliserez le service Buffer pour créer une zone tampon assez grande autour d’une géométrie sélectionnée pour couvrir une part importante de votre carte. Vous pouvez voir le résultat attendu dans la capture d’écran suivante:

../../_images/Mask_Level_121.png

Comme précédemment, vous allez d’abord commencer en écrivant un ZCFG, ensuite vous écrirez le code source JavaScript et finirez par publier votre fournisseur de services.

Le ZCFG

Ouvrez le fichier nommé ~/zoo-ws2013/jschains/cgi-env/Mask.zcfg avec votre éditeur de texte favori et ajoutez le contenu suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[Mask]
 Title = Compute mask
 Abstract = Compute mask around a geometry
 processVersion = 1
 storeSupported = true
 statusSupported = true
 serviceProvider = foss4gws.js
 serviceType = JS
 <DataInputs>
  [InputData]
   Title = The feature
   Abstract = The feature to run the service with
   minOccurs = 1
   maxOccurs = 1
   <ComplexData>
    <Default>
    mimeType = text/xml
    encoding = utf-8
    </Default>
   </ComplexData>
 </DataInputs>
 <DataOutputs>
  [Result]
   Title = The resulting feature
   Abstract = The feature created by the service.
   <ComplexOutput>
     <Default>
     mimeType = application/json
     </Default>
   </ComplexOutput>
 </DataOutputs>

Ici, vous définissez simplement une ComplexData``par défaut, à la fois pour ``inputData et Result: un GML et un GeoJSON respectivement [5].

Le service JavaScript

Comme vous aurez à interroger le service Buffer de nombreuses fois depuis votre service, vous devrez d’abord définir une fonction Buffer comme suit. Il utilise le ZOO.Process pour interroger le service Buffer que vous avez vu dans la section précédente.

Ouvrez un fichier nommé ~/zoo-ws2013/jschains/cgi-env/foss4gws.js et ajoutez le contenu suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var zoo_url='http://localhost/cgi-bin/zoo_loader.cgi';
var mapfile="/var/www/zoows2013-demo/map/w2013_1.map";
var mapserv_url="http://localhost/cgi-bin/mapserv?map="+mapfile;

function Buffer(inputData,bDist){

  // Create all required ZOO.formats
  var fJ=new ZOO.Format.JSON();
  var fGJ=new ZOO.Format.GeoJSON();
  var fWPS=new ZOO.Format.WPS();

  // Pass the value as json
  var myInputs = {
      InputPolygon: { type: 'complex', value: fGJ.write(inputData), mimeType: "application/json"},
      BufferDistance: {type: 'float', "value": bDist }
  };
  var myOutputs= { Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
  var myProcess = new ZOO.Process(zoo_url,'BufferPy');
  var myExecuteResult=myProcess.Execute(myInputs,myOutputs);

  return fGJ.read(myExecuteResult);

}

Des lignes 12 à 15, vous donnez une chaîne GeoJSON (créé à partir de inputData) pour InputPolygon et, sur la ligne 14, vous définissez la valeur de BufferDistance à bDist. Sur la ligne 16, vous définissez Result comme un ResponseDocument, ainsi vous aurez à parser la réponse WPS utilisant le ZOO.Format.WPS, sur la ligne 21.

Sur la ligne 17, vous créez une instance ZOO.Process fournissant l’url du ZOO-Kernel et le nom du service. Puis, à la ligne 18, vous exécutez les requêtes passant les entrées et sorties définies précédemment (des lignes 12 à 15).

Maintenant, vous avez votre fonction Buffer, il est temps de créer votre premier service JavaScript. Donc, éditer votre fichier foss4gws.js créé avant et ajoutez le contenu suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function Mask(conf,inputs,outputs){

  // Create all required ZOO.formats
  var fGML=new ZOO.Format.GML();
  var fGJ=new ZOO.Format.GeoJSON();

  // Read the input GML
  var inputData=fGML.read(inputs["InputData"]["value"]);

  // Compute Buffer
  var bufferResultAsJSON=Buffer(inputData,0.015);

  // Create the Buffer result BBOX and store its geometry in a ZOO.Feature
  var bbox = new ZOO.Bounds();
  var bounds=bufferResultAsJSON[0].geometry.getVertices();
  for(var t in bounds){
    bbox.extend(bounds[t]);
  }
  var finalG=bbox.toGeometry();
  var result=new ZOO.Feature(finalG,{"name": "Result1000"});

  // Return the created feature
  return {
      result: ZOO.SERVICE_SUCCEEDED,
      outputs: { "Result": { mimeType: "application/json", value: fGJ.write(result) } }
  };

}

Publier et utiliser votre service

Maintenant vous avez à la fois votre ZCFG et votre code de service prêt, vous devez déployer votre nouveau fournisseur de services en utilisant la commande suivante:

cp ~/zoo-ws2013/jschains/cgi-env/* /usr/lib/cgi-bin

Maintenant, vous êtes prêt à utiliser votre service JavaScript en chargeant l`url <http://localhost/zoows2013-demo/spatialtools.html>`__ suivante, cliquez sur une rue, puis cliquez sur le bouton “Mask”.

Service BufferMask

Dans cette section, vous implémenterez un service simple JavaScript qui sera capable de créer un trou dans le masque que vous avez créé dans la section précédente. Ce service sera utilisé pour mettre en évidence la zone tampon autour d’un objet sélectionné. Vous avez un aperçu du résultat attendu dans la capture d’écran suivante:

../../_images/BufferMask_Level_151.png

Le ZCFG

Ouvrez le fichier nommé ~/zoo-ws2013/jschains/cgi-env/BufferMask.zcfg avec votre éditeur de texte favori et copiez / collez le contenu suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[BufferMask]
 Title = Compute buffer mask
 Abstract = Compute buffer mask around a geometry
 processVersion = 1
 storeSupported = true
 statusSupported = true
 serviceProvider = foss4gws.js
 serviceType = JS
 <DataInputs>
  [InputData]
   Title = The feature
   Abstract = The feature to run the service with
   minOccurs = 1
   maxOccurs = 1
   <ComplexData>
    <Default>
    mimeType = text/xml
    encoding = utf-8
    </Default>
   </ComplexData>
 </DataInputs>
 <DataOutputs>
  [Result]
   Title = The resulting feature
   Abstract = The feature created by the service.
   <ComplexOutput>
     <Default>
     mimeType = application/json
     </Default>
   </ComplexOutput>
 </DataOutputs>

Ce ZCFG est similaire au précédent. Merci de vous référer aux commentaires de la section précédente pour plus d’informations.

Le service JavaScript

Dans ce service, vous utiliserez le même code source (jusqu’à la ligne 19) que vous avez utilisé dans la section précédente <#the-javascript-service>`__. En effet, vous devez calculer le masque (Mask) comme vous l’avez fait avant puis calculer le tampon (Buffer) pour créer un trou dans le masque (à la ligne 22) pour exécuter le service Difference (des lignes 25 à 40).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 function BufferMask(conf,inputs,outputs){

   // Create all required ZOO.formats
   var fGML=new ZOO.Format.GML();
   var fGJ=new ZOO.Format.GeoJSON();

   // Read the input GML
   var inputData=fGML.read(inputs["InputData"]["value"]);

   // Compute Buffer
   var bufferResultAsJSON=Buffer(inputData,0.015);

   // Create the Buffer result BBOX
   var bbox = new ZOO.Bounds();
   var bounds=bufferResultAsJSON[0].geometry.getVertices();
   for(var t in bounds){
     bbox.extend(bounds[t]);
   }
   var finalG=bbox.toGeometry();

  // Compute Buffer standard buffer
  var bufferResultAsJSON=Buffer(inputData,0.0015);

  // Request Difference service using Buffer result and features in the BBOX
  var result=new ZOO.Feature(finalG,{"name": "Result1000"});
  var myProcess2 = new ZOO.Process(zoo_url,'DifferencePy');
  var myInputs2 = {
      InputEntity1: {
          type: 'complex',
          value: fGJ.write(finalG),
          mimeType: "application/json"
      },
      InputEntity2: {
          type: 'complex',
          value: fGJ.write(bufferResultAsJSON),
          mimeType: "application/json"
      }
  };
  var myOutputs2= {Result: {type: 'RawDataOutput',  mimeType: "application/json" } };
  var myExecuteResult4=myProcess2.Execute(myInputs2,myOutputs2);

   // Return the bbox
   var result=new ZOO.Feature(finalG,{"name": "Result1000"});
   return {
       result: ZOO.SERVICE_SUCCEEDED,
       outputs: { "Result": {mimeType: "application/json", value: myExecuteResult4 } }
   };

 }

Publier et utiliser votre service

Maintenant, vous pouvez publier votre service comme vous l’avez fait avant. Pour utiliser votre service, merci d’utiliser l’url suivante.

Service BufferRequest

Dans cette section, vous allez créer un nouveau service: BufferRequest qui interrogera les POIs inclus dans le tampon (Buffer) autour d’un objet sélectionné [6]. Vous allez utiliser la couche poi servie comme WFS par votre installation locale de MapServer. Vous pouvez voir dans la capture d’écran suivante, le résultat attendu:

../../_images/BufferRequest_Level_151.png

Le ZCFG

Ouvrez le fichier nommé ~/zoo-ws2013/jschains/cgi-env/BufferRequest.zcfg avec votre éditeur de texte favori et copiez / collez le contenu suivant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[BufferRequest]
 Title = Compute buffer request
 Abstract = Compute buffer request around a geometry
 processVersion = 1
 storeSupported = true
 statusSupported = true
 serviceProvider = foss4gws.js
 serviceType = JS
 <DataInputs>
  [InputData]
   Title = The feature
   Abstract = The feature to run the service with
   minOccurs = 1
   maxOccurs = 1
   <ComplexData>
    <Default>
    mimeType = text/xml
    encoding = utf-8
    </Default>
   </ComplexData>
 </DataInputs>
 <DataOutputs>
  [Result]
   Title = The resulting feature
   Abstract = The feature created by the service.
   <ComplexOutput>
     <Default>
     mimeType = application/json
     </Default>
   </ComplexOutput>
 </DataOutputs>

Le code JavaScript

Comme dans le service précédent, vous allez calculer un tampon autour de l’objet en entrée. Mais ensuite vous allez interroger les POI disponibles dans l’étendue du tampon en utilisant une requête WFS pour les utiliser afin d’exécuter un service Intersection en utilisant le tampon initial. La requête WFS est utile pour limiter le nombre de points à utiliser quand vous interrogez le service Intersection.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function BufferRequest(conf,inputs,outputs){

  // Create all required ZOO.formats
  var fGJ=new ZOO.Format.GeoJSON();
  var fGML=new ZOO.Format.GML();

  // Read the input GML
  var inputData=fGML.read(inputs["InputData"]["value"]);

  // Compute Buffer
  var bufferResultAsJSON=Buffer(inputData,0.0015);

  // Create the Buffer result BBOX
  var bbox = new ZOO.Bounds();
  var bounds=bufferResultAsJSON[0].geometry.getVertices();
  for(var t in bounds){
    bbox.extend(bounds[t]);
  }

  // Request Intersection service using Buffer result and WFS request using the
  // BBOX
  var myProcess2 = new ZOO.Process(zoo_url,'Intersection');
  var req="&amp;version=1.0.0&amp;request=GetFeature&amp;typename=poi1";
  req+="&amp;SRS=EPSG:4326&amp;BBOX=";
  var myInputs2 = {
    InputEntity1: {
      type: 'complex',
      value: fGJ.write(bufferResultAsJSON),
      mimeType: "application/json"
    },
    InputEntity2: {
      type: 'complex',
      xlink: mapserv_url+req+bbox.left+","+bbox.bottom+","+bbox.right+","+bbox.top,
      mimeType: "text/xml"
    }
  };
  var myOutputs2= {Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
  var myExecuteResult4=myProcess2.Execute(myInputs2,myOutputs2);

  return {
    result: ZOO.SERVICE_SUCCEEDED,
    outputs: [ {name:"Result", mimeType: "application/json", value: myExecuteResult4} ]
  };

}

Warning

pour tirer avantage du système de cache du ZOO-Kernel, vous utilisez directement la requête WFS comme xlink:href plutôt que la valeur pour InputEntity2 (des lignes 31 à 34) et utilisez le mimeType text/xml (à la ligne 40). En effet, la ZOO-API n’utilise pas les mécanismes de cache interne.

Publier et utiliser votre service

Maintenant, vous pouvez publier votre service comme vous l’avez fait avant. Pour utiliser votre service, merci d’utiliser l’url suivante.

Note

Vous pouvez cliquer sur “Buffer Request and Mask” pour obtenir le même résultat que celui présenté dans la capture d’écran initiale.

Ajouter Union dans la chaîne

Comme vous pouvez le voir dans la capture d’écran suivante, lors de l’utilisation du service Buffer en utilisant une collection d’objets contenant plus d’une géométrie, le résultat est composé de plusieurs géométries. Ainsi, l’exécution d’un service Buffer sur l’interface de routage se traduira par un tampon multiple:

../../_images/Buffer_Routing_Level_15.png

Donc, pour obtenir le même résultat que celui que vous avez obtenu lors de la sélection d’une seule route, vous devez utiliser une Union de géométrie (entrée ou celle retournée par le service Buffer). Comme vous utilisez la ZOO-API JavaScript, vous pouvez simplement mettre à jour la fonction JavaScript Buffer que vous avez défini plus tôt, pour appeler d’abord l’Union de chaque géométrie disponible, dans une collection d’objets avant d’interroger (ou après avoir interrogé) le service Buffer. Heureusement, il ya déjà ce service Python disponible, son nom est UnionOne1, ainsi vous avez juste besoin de l’ajouter dans votre chaîne de service.

Voici le code final pour la fonction JavaScript de Buffer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function Buffer(inputData,bDist){

  // Create all required ZOO.formats
  var fJ=new ZOO.Format.JSON();
  var fGJ=new ZOO.Format.GeoJSON();
  var fWPS=new ZOO.Format.WPS();

  // Call the UnionOne1 Service
  var myInputs0 = {
      InputPolygon: { type: 'complex', value: fGJ.write(inputData), mimeType: "application/json"},
      BufferDistance: {type: 'float', "value": bDist }
  };
  var myOutputs0= { Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
  var myProcess0 = new ZOO.Process(zoo_url,'UnionOne1');
  var myExecuteResult0=myProcess0.Execute(myInputs0,myOutputs0);

  // Call the BufferPy Service
  var myInputs = {
      InputPolygon: { type: 'complex', value: myExecuteResult0, mimeType: "application/json"},
      BufferDistance: {type: 'float', "value": bDist }
  };
  var myOutputs= { Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
  var myProcess = new ZOO.Process(zoo_url,'BufferPy');
  var myExecuteResult=myProcess.Execute(myInputs,myOutputs);

  return fGJ.read(myExecuteResult);

}

Conclusion

Après avoir compris comment les services d’opérations géométriques basiques fonctionnent, ici vous allez construire étape par étape de nouveaux services JavaScript qui réutilisent les précédents et les combinent de différentes façons. Ceci a été réalisé en utilisant la ZOO-API, composée par des fonctions C exposés par le ZOO-Kernel à l’environnement des services d’exécution JavaScript et des fichiers JS qui peuvent être installés en option.

Notes de pied de page

[1]

La classe ZOO.Process utilise JSRequest (cf. ref). Vous aurez un exemple d’utilisation plus tard.

[2]

Ainsi conf, inputs et outputs sont de simples objets JavaScript, similaires aux dictionnaires Python utilisés dans la section précédente.

[3]

Comme conf, inputs et outputs.

[4]

Vous pouvez également retourner un objet de configuration si vous obtenez des informations mises à jour depuis votre service JavaScript (tels que des cookies par exemple)

[5]

En utilisant un des ZOO.formats disponibles, vous êtes également capable de supporter divers ComplexData à la fois en entrée et en sortie du service. Pour simplifier la présentation ici, vous utiliserez seulement ceux par défaut.

[6]

Ainsi, dans le trou que vous avez créé dans la section précédente.