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