Créer des Services Web JavaScript¶
Table des matières
Introduction¶
Cette section illustre comment vous pouvez construire simplement en JavaScript des services en réutilisant des services existants et en les chaînants ensembles 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 en utilisant l’API ZOO. L’objectif final est d’interroger tous les POI inclus dans un tampon autour d’un objet linéaire et de les mettre en évidence à l’aide d’un masque autour de ce tampon. La capture d’écran ci-dessous vous montre le résultat attendu:
Vous pouvez décomposer le résultat ci-dessus en 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é BufferRequest
.
Mais avant d’implémenter tout service JavaScript, nous présentons un rapidement la façon dont l’API ZOO doit être utilisée.
Comme précédemment, nous créons en premier un nouveau répertoire pour stocker les fichiers de notre nouveau fournisseur de services :
mkdir -p ~/jschains/
Aperçu de l’API ZOO¶
Le support JavaScript du ZOO-Kernel vous donne la possibilité 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 et à l’exécuter en lui fournissant les paramètres
pré-remplis par ses soins. 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 chargera l’API ZOO
et Proj4js avant votre service. Dans ce cas, vous
pourrez donc accéder aux classes définies dans l’API ZOO JavaScript pour
manipuler des données géographiques. Pour plus d’informations, merci
de vous référer à la Documentation de l’API ZOO.
Même si il peut être utile pour exécuter le JavaScript côté serveur,
vous devriez vous rappeler que certaines fonctions de base JavaScript
que vous avez l’habitude d’utiliser 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 comme 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 non plus de XMLHttpRequest disponible dans l’environnement
JavaScript où votre service s’exécute. Heureusement, le ZOO-Kernel
expose une fonction C à l’environnement JavaScript nommée: JSRequest
. Cette
fonction vous permet depuis vos services JavaScript d’appeler
d’autres services WPS (locaux ou distants) ou d’autres types de
services OGC tels que le WFS. Lorsque vous utilisez l’API ZOO, 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).
En comparant avec les services Python que vous avez déjà vu dans les sections
précédentes, les fonctions correspondant à un service doivent
toujours prendre trois paramètres en JavaScript : conf
, inputs
et outputs
[2]. Néanmoins, comme
le ZOO-Kernel n’est pas en mesure d’accéder aux espaces mémoires modifiés [3]
par le code de service JavaScript, plutôt que de simplement retourner un entier comme en
Python, vous devrez ici retourner à la fois une valeur de nombre entier
représentant le statut de votre service et
les valeurs outputs
résultantes dans un objet JavaScript [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 } }
};
}
Afin d’implémenter les services dont nous aurons besoin
pour obtenir notre service BufferRequest
final, nous allons
commencer par le plus simple.
Le service Mask¶
Dans cette section, nous apprendrons comment créer notre premier service JavaScript qui renvoit simplement un masque rectangulaire autour d’un objet sélectionné. Pour construire ce masque, nous utiliserons 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 notre carte. Vous pouvez voir le résultat attendu dans la capture d’écran suivante:
Comme précédemment, nous allons d’abord commencer par créer un ZCFG, ensuite nous créerons le code source JavaScript et finirons par le déploiement de notre fournisseur de services.
Le ZCFG¶
Ouvrez le fichier nommé ~/jschains/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 un ComplexData
par défaut, à la fois
pour inputData
et Result
: un GML et un GeoJSON respectivement [5].
Le Service JavaScript¶
Comme nous allons interroger le service Buffer de nombreuses fois
dans nos différents services, nous commençons donc par définir une fonction Buffer
comme suit. Elle utilise le ZOO.Process
pour interroger le service
Buffer que vous avez vu dans la section précédente.
Ouvrez un fichier nommé ~/jschains/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/data/maps/project_WS2014.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);
}
|
De la ligne 12 à la ligne 15, vous donnez une chaîne GeoJSON (créé à partir de
inputData
) pour InputPolygon
et, à la ligne 14, vous affectez à
BufferDistance
la valeur bDist
. À la ligne 16, vous spécifiez
que vous souhaitez récupérer Result
sous la forme d’un
RawDataOutput
, ainsi vous n’aurez pas à parser la réponse
WPS.
À 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 la requête en passant les entrées et sorties définies précédemment (des lignes 12 à 15).
Maintenant que vous avez votre fonction Buffer
, il est temps de créer
votre premier service JavaScript. Donc, éditez votre fichier
foss4gws.js
créé précédemment et ajoutez-y 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 que vous avez à la fois votre ZCFG et le code de votre service de prêt, vous devez le déployer en utilisant la commande suivante:
cp ~/jschains/* /usr/lib/cgi-bin
Vous êtes maintenant prêt à utiliser votre service JavaScript en chargeant cet url, cliquez sur une rue, puis cliquez sur le bouton “Mask”.
Le Service BufferMask¶
Dans cette section, nous implémenterons 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:
Le ZCFG¶
Ouvrez le fichier nommé ~/jschains/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. 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 à 32).
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 cet url.
Les Service BufferRequest¶
Dans cette section, nous allons créer un nouveau service : BufferRequest
qui interrogera les POIs inclus dans le tampon (Buffer) autour d’un
objet sélectionné [6]. Nous utiliserons la couche points1
servie en
WFS par l’installation locale de MapServer. Vous pouvez voir dans
la capture d’écran suivante, le résultat attendu :
Le ZCFG¶
Ouvrez le fichier nommé
~/jschains/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 Service JavaScript¶
Comme dans le service précédent, nous allons calculer un tampon autour
de l’objet en entrée. Mais ensuite nous allons interroger les POI
disponibles dans l’étendue du tampon en utilisant une requête WFS pour
les utiliser afin d’exécuter le service Intersection
en utilisant le
tampon initial. La requête WFS est utile pour limiter le nombre de
points à utiliser avan d’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="&SERVICE=WFS&version=1.0.0&request=GetFeature&typename=poi1";
req+="&SRS=EPSG:4326&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,
nous utilisons directement la requête WFS comme xlink:href
plutôt que
la valeur pour InputEntity2
(des lignes 31 à 34) et spécifions le
mimeType text/xml
(à la ligne 40). En effet, l’API ZOO 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 cet url.
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¶
Si vous consultez le lien suivant : http://localhost/zoo-demo-2016/routing.html vous devriez aussi pouvoir utiliser vos services depuis cette application.
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:
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éfinie 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 y a 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 avez construit de nouveaux services JavaScript qui réutilisent ceux vu précédemment et les combinent de différentes façons. Ces services on été implémentés en utilisant l’API ZOO, composée par des fonctions C exposées par le ZOO-Kernel à l’environnement d’exécution des services JavaScript et des fichiers qui composent l’API-ZOO.
Footnotes
[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. |