source: trunk/docs/workshop/2014/js_services_chaining.txt @ 615

Last change on this file since 615 was 574, checked in by djay, 10 years ago

Add 2014 workshop material.

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

Search

Context Navigation

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