source: trunk/docs/workshop/2012/js_services_chaining.txt @ 522

Last change on this file since 522 was 358, checked in by djay, 13 years ago

Add the 2012 workshop material.

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