source: trunk/workshop/2013/js_services_chaining.rst @ 725

Last change on this file since 725 was 725, checked in by djay, 9 years ago

Fix issue with rst files displayed from Trac. Fix strings to be translated. Always use the same string in all messages.

  • Property svn:keywords set to Date Author
  • Property svn:mime-type set to text/plain
File size: 21.7 KB
Line 
1.. _ogr_base_vect_ops:
2
3Playing with buildign blocks - Creating JavaScript Web Services
4=========================================
5
6.. contents:: Table of Contents
7    :depth: 5
8    :backlinks: top
9
10Introduction
11-------------------------------------------
12
13
14This section illustrate how you can use JavaScript on the server-side to chain
15services together to build new ones. You will create a ZOO Services Provider
16using the services you seen before and the WFS server using the ZOO-API. The
17final goal is to query all POIs included in a buffer around a feature and
18to highlight them using a mask around this buffer. The following screenshot show
19you the expected result:
20
21.. image:: ./images/BufferMaskAndRequest_Level_15.png
22   :width: 650px
23   :align: center
24
25For the routing interface result should look like this:
26
27.. image:: ./images/BufferMaskAndRequest_Routing_Level_15.png
28   :width: 650px
29   :align: center
30
31You can decompose the result above in two different ones: the mask around the
32buffer and the points included in the buffer. So you will create two different
33Services: one called ``BufferMask`` and another one called ``BufferRequest``.
34
35But before implementing any JavaScript Service, you will get an overview of how
36to use ZOO-API from your ZOO-Project installation in the next section.
37
38As before, you first create a new directory to store files for your new Services
39Provider:
40
41.. code-block:: guess
42   
43    mkdir -p ~/zoo-ws2013/jschains/cgi-env/
44
45
46ZOO-API Overview
47-------------------------------------------
48
49ZOO-API and ZOO-Kernel JavaScript support make you able to run services
50implemented in JavaScript on the server side. JavaScript is a popular programing
51language but mostly used on the client side. Let say from a browser, but here it
52is a bit different.
53
54To support JavaScript language ZOO-Kernel use the
55`SpiderMonkey <https://developer.mozilla.org/en/SpiderMonkey>`__ API to create a
56javascript runtime environment from which it will load your JS file then extract
57the function corresponding to the service to run it using the prefilled
58parameters. The JavaScript runtime environment created by the ZOO-Kernel
59depend on your setup. If you placed the ``ZOO-api.js`` and ``ZOO-proj4js.js`` in
60the same directory as your ZOO-Kernel it means that your environment will
61contains ZOO-API and Proj4js will be loaded before your service. In such case you can access to the Classes defined in the JavaScript ZOO-API
62to manipulate geographic data, for more informations please refer to the
63`ZOO-API Documentation <http://zoo-project.org/docs/api/index.html>`__.
64
65Even if it can be useful to run JavaScript on the server side, you should
66remember that some basic JavaScript functions you are familiar with does not
67exist or get a different behavior. For instance the simple ``alert``
68function will display messages in apache error logs rather than in a window when
69used from a browser. The ``alert`` function can be used as follow:
70
71.. code-block:: guess
72
73    alert("My alert message");
74
75There is no XMLHttpRequest available in the JavaScript evironement your service
76will run into. Hopefully, the ZOO-Kernel expose a C function to the JavaScript
77world named: ``JSRequest``. This function make you able from your JavaScript
78services to call other WPS services (locally or remotelly) or other kind OGC
79services such as WFS. When you are using the ZOO-API it is possible to call
80Services using a ``ZOO.Process`` instance [#f3]_, to parse WPS Responses using
81``ZOO.Format.WPS``
82(cf. `ref <http://zoo-project.org/docs/api/zoo-process.html>`__).
83
84As for Python services you already seen in previous sections, the functions
85corresponding to a Service should take three arguments: ``conf``, ``inputs`` and
86``outputs`` [#f4]_. Nevertheless, as the ZOO-Kernel is not able to access the
87values modified [#f5]_ by the Service code, rather than returning an integer as
88in Python, here you'll need to return both the integer value representing the
89Status of your Service in a JavaScript Object and the resulting ``outputs``
90values as an Object [#f6]_. You can see in the following an example of a JavaScript
91Service code:
92
93.. code-block:: none
94   
95    function SampleService(conf,inputs,outputs){
96      var resultValue=someComputation(inputs);
97      return
98        {
99            result: ZOO.SERVICE_SUCCEEDED,
100            outputs: { "Result": { "mimeType": "application/json", "value": resultValue } }
101        };
102    }
103
104Before starting to implement the Services we will need to get our final
105BufferRequest service, let start with a simplier one.
106
107The Mask Service
108-------------------------------------------
109
110In this section you will learn how to create your first JavaScript service which
111will simply return a rectangular mask around a selected feature. To build this
112mask you will use the Buffer service to create a buffer big enough around a
113selected geometry to cover a significant part of your map. You can see the
114expected result in the following screenshot:
115
116.. image:: ./images/Mask_Level_12.png
117   :width: 650px
118   :align: center
119
120As before, you will first start by writting the ZCFG, then you will write the
121JavaScript source code and finish by publishing your Services Provider.
122
123The ZCFG
124.....................................
125
126Open the file named
127``~/zoo-ws2013/jschains/cgi-env/Mask.zcfg``
128with your favorite text editor and add the following content:
129
130.. code-block:: none
131    :linenos:
132   
133    [Mask]
134     Title = Compute mask
135     Abstract = Compute mask around a geometry
136     processVersion = 1
137     storeSupported = true
138     statusSupported = true
139     serviceProvider = foss4gws.js
140     serviceType = JS
141     <DataInputs>
142      [InputData]
143       Title = The feature
144       Abstract = The feature to run the service with
145       minOccurs = 1
146       maxOccurs = 1
147       <ComplexData>
148        <Default>
149        mimeType = text/xml
150        encoding = utf-8
151        </Default>
152       </ComplexData>
153     </DataInputs>
154     <DataOutputs>
155      [Result]
156       Title = The resulting feature
157       Abstract = The feature created by the service.
158       <ComplexOutput>
159         <Default>
160         mimeType = application/json
161         </Default>
162       </ComplexOutput>
163     </DataOutputs> 
164
165Here you simply define one default ``ComplexData`` for both ``inputData`` and
166``Result``: a GML and a GeoJSON respectively [#f7]_.
167 
168The JavaScript service
169.....................................
170
171As you will have to request the Buffer service many times from your service, you
172will first define a ``Buffer`` function as follow. It uses the ``ZOO.Process``
173to request the Buffer service you seen in the previous section.
174
175Open a file named
176``~/zoo-ws2013/jschains/cgi-env/foss4gws.js`` and
177add the following content:
178
179.. code-block:: javascript
180    :linenos:
181   
182    var zoo_url='http://localhost/cgi-bin/zoo_loader.cgi';
183    var mapfile="/var/www/zoows2013-demo/map/w2013_1.map";
184    var mapserv_url="http://localhost/cgi-bin/mapserv?map="+mapfile;
185
186    function Buffer(inputData,bDist){
187   
188      // Create all required ZOO.formats
189      var fJ=new ZOO.Format.JSON();
190      var fGJ=new ZOO.Format.GeoJSON();
191      var fWPS=new ZOO.Format.WPS();
192   
193      // Pass the value as json
194      var myInputs = {
195          InputPolygon: { type: 'complex', value: fGJ.write(inputData), mimeType: "application/json"},
196          BufferDistance: {type: 'float', "value": bDist }
197      }; 
198      var myOutputs= { Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
199      var myProcess = new ZOO.Process(zoo_url,'BufferPy');
200      var myExecuteResult=myProcess.Execute(myInputs,myOutputs);
201   
202      return fGJ.read(myExecuteResult);
203   
204    }
205
206From line 12 to 15, you give a GeoJSON string (created from ``inputData``) for
207InputPolygon and, on line 14, you set the BufferDistance value to ``bDist``.
208On line 16, you define Result as a ResponseDocument, so you'll have to parse the
209WPS response using the ZOO.Format.WPS, on line 21.
210
211On line 17, you create a
212`ZOO.Process <http://zoo-project.org/docs/api/zoo-process.html#zoo-process>`__
213instance providing the ZOO-Kernel url and the Service name. Then, on line 18, you
214run the request passing inputs and outputs previously defined (from line 12 to 15).
215
216Now, you get your ``Buffer`` function, it is time to create your first JavaScript
217service. So, edit your ``foss4gws.js`` file you created before and add the following content:
218
219.. code-block:: javascript
220    :linenos:
221   
222    function Mask(conf,inputs,outputs){
223     
224      // Create all required ZOO.formats
225      var fGML=new ZOO.Format.GML();
226      var fGJ=new ZOO.Format.GeoJSON();
227   
228      // Read the input GML
229      var inputData=fGML.read(inputs["InputData"]["value"]);
230     
231      // Compute Buffer
232      var bufferResultAsJSON=Buffer(inputData,0.015);
233   
234      // Create the Buffer result BBOX and store its geometry in a ZOO.Feature
235      var bbox = new ZOO.Bounds();
236      var bounds=bufferResultAsJSON[0].geometry.getVertices();
237      for(var t in bounds){
238        bbox.extend(bounds[t]);
239      }
240      var finalG=bbox.toGeometry();
241      var result=new ZOO.Feature(finalG,{"name": "Result1000"});
242     
243      // Return the created feature
244      return {
245          result: ZOO.SERVICE_SUCCEEDED,
246          outputs: { "Result": { mimeType: "application/json", value: fGJ.write(result) } }
247      };
248     
249    }
250
251Publish and use your Service
252..............................................
253
254Now you get both your ZCFG and your service code ready, you need to deploy your
255new Services Provider using the following command:
256
257.. code-block:: bash
258   
259    cp ~/zoo-ws2013/jschains/cgi-env/* /usr/lib/cgi-bin
260
261Now you are ready to use your JavaScript service by loading the following `url
262<http://localhost/zoows2013-demo/spatialtools.html>`__, click on a street then click on
263the "Mask" button.
264
265BufferMask Service
266-------------------------------------------
267
268In this section you will implement a simple JavaScript service which will be able create
269a hole in the mask you created in `previous section <#mask-service>`__. This service
270will be used to highlight the buffer zone around a selected fature. You get a preview of
271the expected result in the following screenshot:
272
273.. image:: ./images/BufferMask_Level_15.png
274   :width: 650px
275   :align: center
276
277
278The ZCFG
279..............................................
280
281Open the file named
282``~/zoo-ws2013/jschains/cgi-env/BufferMask.zcfg``
283with your favorite text editor and copy / paste the following content:
284
285.. code-block:: none
286    :linenos:
287   
288    [BufferMask]
289     Title = Compute buffer mask
290     Abstract = Compute buffer mask around a geometry
291     processVersion = 1
292     storeSupported = true
293     statusSupported = true
294     serviceProvider = foss4gws.js
295     serviceType = JS
296     <DataInputs>
297      [InputData]
298       Title = The feature
299       Abstract = The feature to run the service with
300       minOccurs = 1
301       maxOccurs = 1
302       <ComplexData>
303        <Default>
304        mimeType = text/xml
305        encoding = utf-8
306        </Default>
307       </ComplexData>
308     </DataInputs>
309     <DataOutputs>
310      [Result]
311       Title = The resulting feature
312       Abstract = The feature created by the service.
313       <ComplexOutput>
314         <Default>
315         mimeType = application/json
316         </Default>
317       </ComplexOutput>
318     </DataOutputs> 
319
320This ZCFG is similar to the previous one. Please, refer to comments in the
321`previous section <#the-zcfg>`__ for more informations.
322
323The JavaScript service
324..............................................
325
326In this Service you will use same source code (until line 19) you used in the
327`previous section <#the-javascript-service>`__. Indeed, you should compute the Mask
328as you did before then compute Buffer for creating a hole in the mask (on line 22) to run
329the Difference service (from line 25 to 40).
330
331.. code-block:: guess
332    :linenos:
333   
334     function BufferMask(conf,inputs,outputs){
335       
336       // Create all required ZOO.formats
337       var fGML=new ZOO.Format.GML();
338       var fGJ=new ZOO.Format.GeoJSON();
339     
340       // Read the input GML
341       var inputData=fGML.read(inputs["InputData"]["value"]);
342       
343       // Compute Buffer
344       var bufferResultAsJSON=Buffer(inputData,0.015);
345     
346       // Create the Buffer result BBOX
347       var bbox = new ZOO.Bounds();
348       var bounds=bufferResultAsJSON[0].geometry.getVertices();
349       for(var t in bounds){
350         bbox.extend(bounds[t]);
351       }
352       var finalG=bbox.toGeometry();
353
354      // Compute Buffer standard buffer
355      var bufferResultAsJSON=Buffer(inputData,0.0015);
356   
357      // Request Difference service using Buffer result and features in the BBOX
358      var result=new ZOO.Feature(finalG,{"name": "Result1000"}); 
359      var myProcess2 = new ZOO.Process(zoo_url,'DifferencePy');
360      var myInputs2 = {
361          InputEntity1: {
362              type: 'complex',
363              value: fGJ.write(finalG),
364              mimeType: "application/json"
365          },
366          InputEntity2: {
367              type: 'complex',
368              value: fGJ.write(bufferResultAsJSON),
369              mimeType: "application/json"
370          }
371      };
372      var myOutputs2= {Result: {type: 'RawDataOutput',  mimeType: "application/json" } };
373      var myExecuteResult4=myProcess2.Execute(myInputs2,myOutputs2);
374
375       // Return the bbox
376       var result=new ZOO.Feature(finalG,{"name": "Result1000"});
377       return {
378           result: ZOO.SERVICE_SUCCEEDED,
379           outputs: { "Result": {mimeType: "application/json", value: myExecuteResult4 } }
380       };
381     
382     }
383
384Publish and use your Service
385..............................................
386
387Now, you can publish your service as you did `before <#publish-your-service>`__. To
388use your service, please use the following `url
389<http://localhost/zoows2013-demo/spatialtools.html>`__.
390
391BufferRequest service
392-------------------
393
394In this section, you will create a new Service: ``BufferRequest`` which will request
395POIs included in the Buffer around a selected feature [#f8]_. You will use the ``poi``
396layer served as WFS through your local mapserver installation. You can see in the
397following screenshot the expected result:
398
399.. image:: ./images/BufferRequest_Level_15.png
400   :width: 650px
401   :align: center
402
403The ZCFG
404..............................................
405
406Open the file named
407``~/zoo-ws2013/jschains/cgi-env/BufferRequest.zcfg``
408with your favorite text editor and copy / paste the following content:
409
410.. code-block:: none
411    :linenos:
412   
413    [BufferRequest]
414     Title = Compute buffer request
415     Abstract = Compute buffer request around a geometry
416     processVersion = 1
417     storeSupported = true
418     statusSupported = true
419     serviceProvider = foss4gws.js
420     serviceType = JS
421     <DataInputs>
422      [InputData]
423       Title = The feature
424       Abstract = The feature to run the service with
425       minOccurs = 1
426       maxOccurs = 1
427       <ComplexData>
428        <Default>
429        mimeType = text/xml
430        encoding = utf-8
431        </Default>
432       </ComplexData>
433     </DataInputs>
434     <DataOutputs>
435      [Result]
436       Title = The resulting feature
437       Abstract = The feature created by the service.
438       <ComplexOutput>
439         <Default>
440         mimeType = application/json
441         </Default>
442       </ComplexOutput>
443     </DataOutputs> 
444
445
446The JavaScript code
447..............................................
448
449As in the previous Service, you will compute a buffer around the input feature. But then
450you will request POIs available in the Buffer extent using a WFS request to use them to
451run ``Intersection`` service using the initial Buffer. The WFS request is useful to limit
452the number of points to use when requesting the ``Intersection`` Service.
453
454.. code-block:: javascript
455    :linenos:
456   
457    function BufferRequest(conf,inputs,outputs){
458   
459      // Create all required ZOO.formats
460      var fGJ=new ZOO.Format.GeoJSON();
461      var fGML=new ZOO.Format.GML();
462   
463      // Read the input GML
464      var inputData=fGML.read(inputs["InputData"]["value"]);
465   
466      // Compute Buffer
467      var bufferResultAsJSON=Buffer(inputData,0.0015);
468   
469      // Create the Buffer result BBOX
470      var bbox = new ZOO.Bounds();
471      var bounds=bufferResultAsJSON[0].geometry.getVertices();
472      for(var t in bounds){
473        bbox.extend(bounds[t]);
474      }
475   
476      // Request Intersection service using Buffer result and WFS request using the
477      // BBOX
478      var myProcess2 = new ZOO.Process(zoo_url,'Intersection');
479      var req="&amp;version=1.0.0&amp;request=GetFeature&amp;typename=poi1";
480      req+="&amp;SRS=EPSG:4326&amp;BBOX=";
481      var myInputs2 = {
482        InputEntity1: {
483          type: 'complex', 
484          value: fGJ.write(bufferResultAsJSON),
485          mimeType: "application/json"
486        },
487        InputEntity2: {
488          type: 'complex', 
489          xlink: mapserv_url+req+bbox.left+","+bbox.bottom+","+bbox.right+","+bbox.top,
490          mimeType: "text/xml"
491        }
492      };
493      var myOutputs2= {Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
494      var myExecuteResult4=myProcess2.Execute(myInputs2,myOutputs2);
495   
496      return {
497        result: ZOO.SERVICE_SUCCEEDED,
498        outputs: [ {name:"Result", mimeType: "application/json", value: myExecuteResult4} ]
499      };
500   
501    }
502
503.. warning:: to take advantage of the ZOO-Kernel cache system, you directly use the WFS
504    request  as ``xlink:href`` rather than value for ``InputEntity2`` (from line 31 to 34) and use ``text/xml`` ``mimeType``
505    (on line 40). Indeed, the ZOO-API doesn't use the internal cache mechanisms.
506
507
508Publish and use your Service
509..............................................
510
511Now, you can publish your service as you did `before <#publish-your-service>`__. To
512use your service, please use the following `url
513<http://localhost/zoows2013-demo/spatialtools.html>`__.
514
515.. note:: You can click on "Buffer Request and Mask"  to get the same result as presented
516    in  `the initial screenshot <#introduction>`__.
517
518
519Add Union into the chain
520------------------------
521
522As you can see in the following screenshot, when using the Buffer
523service using a feature collection containing more than one geometry,
524the result is made of multiple geometries. So, running Buffer service
525on the routing interface will result in multiple buffer:
526
527.. image:: ./images/Buffer_Routing_Level_15.png
528   :width: 650px
529   :align: center
530
531So, to get the same result as you got when selecting a single
532road, you should use Union of geometry (input or the one outputed by the
533``Buffer`` Service). As you are using the JavaScript ZOO-API, you can
534simply update the ``Buffer`` JavaScript function you defined earlier, to
535first call the Union of each geometry avaible in a feature collection
536prior to request (or after requesting) the Buffer Service. Hopefully,
537there is already this Python Service available, its name is
538``UnionOne1``, so you just need to add it in your Service chain.
539
540Here is the final code for the Buffer JavaScript function:
541
542.. code-block:: javascript
543    :linenos:
544   
545    function Buffer(inputData,bDist){
546   
547      // Create all required ZOO.formats
548      var fJ=new ZOO.Format.JSON();
549      var fGJ=new ZOO.Format.GeoJSON();
550      var fWPS=new ZOO.Format.WPS();
551   
552      // Call the UnionOne1 Service
553      var myInputs0 = {
554          InputPolygon: { type: 'complex', value: fGJ.write(inputData), mimeType: "application/json"},
555          BufferDistance: {type: 'float', "value": bDist }
556      }; 
557      var myOutputs0= { Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
558      var myProcess0 = new ZOO.Process(zoo_url,'UnionOne1');
559      var myExecuteResult0=myProcess0.Execute(myInputs0,myOutputs0);
560
561      // Call the BufferPy Service
562      var myInputs = {
563          InputPolygon: { type: 'complex', value: myExecuteResult0, mimeType: "application/json"},
564          BufferDistance: {type: 'float', "value": bDist }
565      }; 
566      var myOutputs= { Result: { type: 'RawDataOutput', "mimeType": "application/json" } };
567      var myProcess = new ZOO.Process(zoo_url,'BufferPy');
568      var myExecuteResult=myProcess.Execute(myInputs,myOutputs);
569   
570      return fGJ.read(myExecuteResult);
571   
572    }
573
574   
575Conclusion
576--------------
577
578After understanding how basic Geometric Operation Services works, here you built step by step new JavaScript services which reuse the previous ones and combine them in different ways. This was achieved using the ZOO-API, composed by C functions exposed by the ZOO-Kernel to the JavaScript services runtime environement and the JS files which can be optionally installed.
579
580.. rubric:: Footnotes
581
582.. [#f3] The ``ZOO.Process`` class uses ``JSRequest`` (cf. `ref
583    <http://zoo-project.org/docs/api/zoo-format-wps.html>`__). You will get example
584    of use `later  <#the-javascript-service>`__.
585.. [#f4] So ``conf``, ``inputs`` and ``outputs`` are simple JavaScript objects, similar
586    to the Python dictionaries used in the `previous section <ogr_base_vect_ops.html>`__.   
587.. [#f5] Such as ``conf``, ``inputs`` and ``outputs``.
588.. [#f6] You can also return a conf Object if you get any informations updated from your JavaScript service (such as cookie for instance)
589.. [#f7] Using one of the available ``ZOO.formats`` you are also able to support various
590    ``ComplexData`` for both input and output of the service. To simplify the
591    presentation here, you will use only this default ones.
592.. [#f8] So in the hole you created in the previous section.
Note: See TracBrowser for help on using the repository browser.

Search

ZOO Sponsors

http://www.zoo-project.org/trac/chrome/site/img/geolabs-logo.pnghttp://www.zoo-project.org/trac/chrome/site/img/neogeo-logo.png http://www.zoo-project.org/trac/chrome/site/img/apptech-logo.png http://www.zoo-project.org/trac/chrome/site/img/3liz-logo.png http://www.zoo-project.org/trac/chrome/site/img/gateway-logo.png

Become a sponsor !

Knowledge partners

http://www.zoo-project.org/trac/chrome/site/img/ocu-logo.png http://www.zoo-project.org/trac/chrome/site/img/gucas-logo.png http://www.zoo-project.org/trac/chrome/site/img/polimi-logo.png http://www.zoo-project.org/trac/chrome/site/img/fem-logo.png http://www.zoo-project.org/trac/chrome/site/img/supsi-logo.png http://www.zoo-project.org/trac/chrome/site/img/cumtb-logo.png

Become a knowledge partner

Related links

http://zoo-project.org/img/ogclogo.png http://zoo-project.org/img/osgeologo.png