1 | .. _ogr_base_vect_ops: |
---|
2 | |
---|
3 | Creating OGR based Web Services |
---|
4 | ====================================================== |
---|
5 | |
---|
6 | .. contents:: Table of Contents |
---|
7 | :depth: 5 |
---|
8 | :backlinks: top |
---|
9 | |
---|
10 | Introduction |
---|
11 | -------------------------------------------------------------------- |
---|
12 | |
---|
13 | In this part, we are going to create a ZOO ServicesProvider containing several Services |
---|
14 | based on the OGR C API or on the OGR Python module, which have also been placed in the |
---|
15 | ZOO installation on OSGeoLive. The intended goal is to use OGR and its GEOS based simple |
---|
16 | spatial functions as WPS Services. |
---|
17 | |
---|
18 | We will first start with the Boundary spatial function, which will be explained, codded |
---|
19 | and tested gradually as a ZOO Service. The same procedure will then be used to enable |
---|
20 | the Buffer, Centroid and Convex Hull functions. Once done, some multiple geometries processes |
---|
21 | such as Intersection, Union, Difference and Symetric Difference will be implemented through |
---|
22 | an `exercise <./exercise.html>`__ at the end of the workshop. |
---|
23 | |
---|
24 | As already said in the introduction, you have the choice to code your service in C or |
---|
25 | Python (or both!) during this workshop. Explanations will be based on the C part, but |
---|
26 | will be very helpful for those who will choose Python. Please decide according to your |
---|
27 | habits and preferences and tell your choice to the instructors. The results will be the |
---|
28 | same in both case. |
---|
29 | |
---|
30 | Preparing ZOO metadata file |
---|
31 | -------------------------------------------------------------------- |
---|
32 | |
---|
33 | A ZOO Service is a combination of a ZOO metadata file (``.zcfg``) and the runtime module |
---|
34 | for the corresponding implementation, which is commonly called ZOO Service Provider. We |
---|
35 | will first prepare a ``.zcfg`` file step-by-step. Please open your preferred text editor |
---|
36 | and edit a file named ``Boundary.zcfg`` in your ``/home/user/zoows/sources/zoo-services/ws_sp`` |
---|
37 | directory. First, you need to name the service between brackets at the top of the file, as the |
---|
38 | following |
---|
39 | |
---|
40 | :: |
---|
41 | |
---|
42 | [Boundary] |
---|
43 | |
---|
44 | This name is very important, it is the name of the Service and so the name of the function |
---|
45 | defined in the Services Provider. A title and a brief abstract must then be added to inform |
---|
46 | clients on what the service can do: |
---|
47 | |
---|
48 | .. code-block:: guess |
---|
49 | |
---|
50 | Title = Compute boundary. |
---|
51 | Abstract = Returns the boundary of the geometry on which the method is invoked. |
---|
52 | |
---|
53 | Such metadata informations will be returned by a GetCapabilities request. |
---|
54 | |
---|
55 | You can also add other specific informations like the ``processVersion``. You can set if |
---|
56 | your ZOO Service can store its results, by setting the ``storeSupported`` parameter to |
---|
57 | true or false. You can also decide if the function can be run as a background task and |
---|
58 | inform on its current status, according to the ``statusSupported`` value : |
---|
59 | |
---|
60 | .. code-block:: guess |
---|
61 | |
---|
62 | processVersion = 1 |
---|
63 | storeSupported = true |
---|
64 | statusSupported = true |
---|
65 | |
---|
66 | In the main section of the ZOO Service metadata file, you must also specify two important things: |
---|
67 | |
---|
68 | - ``serviceProvider``, which is the name of the C shared library containing the Service function or the Python module name. |
---|
69 | - ``serviceType``, which defines the programming language to be used for the Service. (value can be C or Python depending on what language you have decided to use) |
---|
70 | |
---|
71 | C ServicesProvider Example : |
---|
72 | |
---|
73 | .. code-block:: guess |
---|
74 | |
---|
75 | serviceProvider=ogr_ws_service_provider.zo |
---|
76 | serviceType=C |
---|
77 | |
---|
78 | In this case you will get an ``ogr_ws_service_provider.zo`` shared library containing |
---|
79 | the Boundary function, placed in the same directory than ZOO Kernel. |
---|
80 | |
---|
81 | Python ServicesProvider Example : |
---|
82 | |
---|
83 | .. code-block:: guess |
---|
84 | |
---|
85 | serviceProvider=ogr_ws_service_provider |
---|
86 | serviceType=Python |
---|
87 | |
---|
88 | In this case, you will get an ``ogr_ws_service_provider.py`` file containing the Python code of your Boundary function. |
---|
89 | |
---|
90 | In the main section you can also add any other metadata information, as the following: |
---|
91 | |
---|
92 | .. code-block:: guess |
---|
93 | |
---|
94 | <MetaData> |
---|
95 | Title = Demo |
---|
96 | </MetaData> |
---|
97 | |
---|
98 | The main metadata informations have been declared, so you can now define data input |
---|
99 | which will be used by the ZOO Service. You can define any input needed by the Service. |
---|
100 | Please note that you can request ZOO Kernel using more data input than defined in |
---|
101 | the ``.zcfg`` file without any problem, those values will be passed to your service |
---|
102 | without filtering. In the Boundary Service example, a single polygon will be used as |
---|
103 | input, the one on which to apply the Boundary function. |
---|
104 | |
---|
105 | The data input declarations are included in a DataInputs block. They use the same |
---|
106 | syntax as the Service itself and the input name is between brackets. You can also |
---|
107 | fill a title, an abstract and a MetaData section for the input. You must set values |
---|
108 | for the ``minOccurs`` and ``maxOccurs`` parameters, as they will inform ZOO Kernel |
---|
109 | which parameters are required to be able to run the Service function. |
---|
110 | |
---|
111 | .. code-block:: none |
---|
112 | |
---|
113 | [InputPolygon] |
---|
114 | Title = Polygon to compute boundary |
---|
115 | Abstract = URI to a set of GML that describes the polygon. |
---|
116 | minOccurs = 1 |
---|
117 | maxOccurs = 1 |
---|
118 | <MetaData> |
---|
119 | Test = My test |
---|
120 | </MetaData> |
---|
121 | |
---|
122 | |
---|
123 | The metadata defines what type of data the Service supports. In the Boundary example, |
---|
124 | the input polygon can be provided as a GML file or as a JSON string. Next step is |
---|
125 | thus to define the default and supported input formats. Both formats should be declared |
---|
126 | in a LitteralData or ComplexData block depending on their types. For this first example |
---|
127 | we will use ComplexData blocks only. |
---|
128 | |
---|
129 | .. code-block:: guess |
---|
130 | |
---|
131 | <ComplexData> |
---|
132 | <Default> |
---|
133 | mimeType = text/xml |
---|
134 | encoding = UTF-8 |
---|
135 | </Default> |
---|
136 | <Supported> |
---|
137 | mimeType = application/json |
---|
138 | encoding = UTF-8 |
---|
139 | </Supported> |
---|
140 | </ComplexData> |
---|
141 | |
---|
142 | |
---|
143 | Then, the same metadata information must be defined for the output of the Service, inside a DataOutputs block, as the following: |
---|
144 | |
---|
145 | .. code-block:: none |
---|
146 | |
---|
147 | [Result] |
---|
148 | Title = The created geometry |
---|
149 | Abstract = The geometry containing the boundary of the geometry on which the method was invoked. |
---|
150 | <MetaData> |
---|
151 | Title = Result |
---|
152 | </MetaData> |
---|
153 | <ComplexData> |
---|
154 | <Default> |
---|
155 | mimeType = application/json |
---|
156 | encoding = UTF-8 |
---|
157 | </Default> |
---|
158 | <Supported> |
---|
159 | mimeType = text/xml |
---|
160 | encoding = UTF-8 |
---|
161 | </Supported> |
---|
162 | </ComplexData> |
---|
163 | |
---|
164 | A complete copy of this ``.zcfg`` file can be found at the following URL: http://zoo-project.org/trac/browser/trunk/zoo-services/ogr/base-vect-ops/cgi-env/Boundary.zcfg. |
---|
165 | |
---|
166 | |
---|
167 | Once the ZOO metadata file is modified, you have to copy it in the same directory |
---|
168 | than your ZOO Kernel (so in your case ``/usr/lib/cgi-bin``). Then you should be |
---|
169 | able to run the following request : |
---|
170 | |
---|
171 | http://localhost/zoo/?Request=DescribeProcess&Service=WPS&Identifier=Boundary&version=1.0.0 |
---|
172 | |
---|
173 | The returned ProcessDescriptions XML document should look like the following : |
---|
174 | |
---|
175 | .. image:: ./images/Practical-introduction-to-ZOO-5.png |
---|
176 | :width: 456px |
---|
177 | :height: 157px |
---|
178 | :align: center |
---|
179 | |
---|
180 | Please note that the GetCapabilities and DescribeProcess only need a ``.zcfg`` |
---|
181 | file to be completed. Simple, isn't it ? At this step, if you request ZOO Kernel |
---|
182 | for an Execute, you will get an ExceptionReport document as response, looking as the following : |
---|
183 | |
---|
184 | .. image:: ./images/Practical-introduction-to-ZOO-6.png |
---|
185 | :width: 546px |
---|
186 | :height: 80px |
---|
187 | :align: center |
---|
188 | |
---|
189 | A similar error message will be returned if you try to run your Python Service : |
---|
190 | |
---|
191 | .. image:: ./images/Practical-introduction-to-ZOO-7.png |
---|
192 | :width: 489px |
---|
193 | :height: 87px |
---|
194 | :align: center |
---|
195 | |
---|
196 | |
---|
197 | Implementing single geometry services |
---|
198 | ------------------------------------------------------------------------------ |
---|
199 | |
---|
200 | In order to learn the Services Provider creation and deployement step-by-step, |
---|
201 | we will first focus on creating a very simple one dedicated to the Boundary function. |
---|
202 | Similar procedure will then be used for the Buffer, Centroid and ConvexHull implementation. |
---|
203 | |
---|
204 | Your metadata is now ok, so you now must create the code of your Service. The most |
---|
205 | important thing you must be aware of when coding ZOO Services is that the function |
---|
206 | corresponding to your Service takes three parameters (internal maps datatype or |
---|
207 | `Python dictionaries <http://docs.python.org/tutorial/datastructures.html#dictionaries>`__) |
---|
208 | and returns an integer value representing the status of execution (SERVICE_FAILED or SERVICE_SUCCEEDED): |
---|
209 | |
---|
210 | - ``conf`` : The main environment configuration (corresponding to the ``main.cfg`` content) |
---|
211 | - ``inputs`` : The requested / default inputs |
---|
212 | - ``outputs`` : The requested / default outputs |
---|
213 | |
---|
214 | Boundary |
---|
215 | .......................................................................................................................................................... |
---|
216 | |
---|
217 | C Version |
---|
218 | ******************************************************************************************************* |
---|
219 | |
---|
220 | As explained before, ZOO Kernel will pass the parameters to your Service function |
---|
221 | in a specific datatype called maps. In order to code your Service in C language, |
---|
222 | you also need to learn how to access this datatype in read/write mode. |
---|
223 | |
---|
224 | The maps are simple map named linked list containing a name, a content map and a |
---|
225 | pointer to the next map in the list (or NULL if there is no more map in the list). |
---|
226 | Here is the datatype definition as you can find in the zoo-kernel/service.h file: |
---|
227 | |
---|
228 | .. code-block:: c |
---|
229 | |
---|
230 | typedef struct maps{ |
---|
231 | char* name; |
---|
232 | struct map* content; |
---|
233 | struct maps* next; |
---|
234 | } maps; |
---|
235 | |
---|
236 | The map included in the maps is also a simple linked list and is used to store Key |
---|
237 | Value Pair values. A map is thus a couple of name and value and a pointer to the |
---|
238 | next map in the list. Here is the datatype definition you can find in the zoo-kernel/service.h file: |
---|
239 | |
---|
240 | .. code-block:: guess |
---|
241 | |
---|
242 | typedef struct map{ |
---|
243 | char* name; /* The key */ |
---|
244 | char* value; /* The value */ |
---|
245 | struct map* next; /* Next couple */ |
---|
246 | } map; |
---|
247 | |
---|
248 | |
---|
249 | As partially or fully filled datastructures will be passed by the ZOO Kernel to |
---|
250 | your Services, this means that you do not need to deal with maps creation but |
---|
251 | directly with existing map, in other words the content of each maps. The first |
---|
252 | function you need to know is getMapFromMaps (defined in the zoo-kernel/service.h file) |
---|
253 | which let you access to a specific map of a maps. |
---|
254 | |
---|
255 | This function takes three parameters listed bellow: |
---|
256 | |
---|
257 | - ``m`` : a maps pointer representing the maps used to search the specific map |
---|
258 | - ``name`` : a char* representing the name of the map you are searching for |
---|
259 | - ``key`` : a specific key in the map named name |
---|
260 | |
---|
261 | For example, the following syntax will be used to access the InputPolygon value |
---|
262 | map of a maps named inputs, your C code should be: |
---|
263 | |
---|
264 | .. code-block:: guess |
---|
265 | |
---|
266 | map* tmp=getMapFromMaps(inputs,"InputPolygon","value"); |
---|
267 | |
---|
268 | Once you get the map, you can access the name or the value fields, using the following syntax : |
---|
269 | |
---|
270 | .. code-block:: guess |
---|
271 | |
---|
272 | tmp->name |
---|
273 | tmp->value |
---|
274 | |
---|
275 | As you know how to read and access the map fields from a maps, you can now learn |
---|
276 | how to write in such a datastructure. This is done by using the simple setMapInMaps |
---|
277 | function once again defined in zoo-kernel/service.h. The setMapInMaps function takes four parameters : |
---|
278 | |
---|
279 | - ``m`` : a maps pointer you want to update, |
---|
280 | - ``ns`` : the name of the maps you want you want to update, |
---|
281 | - ``n`` : the name of the map you want to add or update the value, |
---|
282 | - ``v`` : the value you want to set for this map. |
---|
283 | |
---|
284 | Here is an example of how to add or edit the values of some map in the Result maps from outputs : |
---|
285 | |
---|
286 | .. code-block:: guess |
---|
287 | |
---|
288 | setMapInMaps(outputs,"Result","value","Hello from the C World !"); |
---|
289 | setMapInMaps(outputs,"Result","mimeType","text/plain"); |
---|
290 | setMapInMaps(outputs,"Result","encoding","UTF-8"); |
---|
291 | |
---|
292 | |
---|
293 | Please note that the setMapInMaps function is able to create or update an existing map. |
---|
294 | Indeed, if a map called « value » allready exists, then its value will be updated automatically. |
---|
295 | |
---|
296 | Even if you will mainly use map from maps during this workshop, you can also add or |
---|
297 | update values in a map directly using the addToMap function defined in zoo-kernel/service.h. |
---|
298 | The addToMap function take three paramters : |
---|
299 | |
---|
300 | - ``m`` : a map pointer you want to update, |
---|
301 | - ``n`` : the name of the map you want to add or update the value, |
---|
302 | - ``v`` : the value you want to set in this map. |
---|
303 | |
---|
304 | This datatype is really important cause it is used in every C based ZOO Services. It is |
---|
305 | also the same representation used in other languages but using their respectives datatypes. |
---|
306 | For Example in Python, the dictionaries datatype is used, so manipulation is much easier. |
---|
307 | |
---|
308 | Here is an example of the correspoding maps datatype used in Python language (this is a |
---|
309 | summarized version of the main configaration maps): |
---|
310 | |
---|
311 | .. code-block:: guess |
---|
312 | |
---|
313 | main={ |
---|
314 | "main": { |
---|
315 | "encoding": "utf-8", |
---|
316 | "version": "1.0.0", |
---|
317 | "serverAddress": "http://www.zoo-project.org/zoo/", |
---|
318 | "lang": "fr-FR,en-CA" |
---|
319 | }, |
---|
320 | "identification": {"title": "The Zoo WPS Development Server", |
---|
321 | "abstract": "Development version of ZooWPS.", |
---|
322 | "fees": "None", |
---|
323 | "accessConstraints": "none", |
---|
324 | "keywords": "WPS,GIS,buffer" |
---|
325 | } |
---|
326 | } |
---|
327 | |
---|
328 | As you know how to deal with maps and map, you are ready to code the first ZOO Service by using the OGR Boundary function. |
---|
329 | |
---|
330 | As already said in introduction we will use the MapServer WFS server available on |
---|
331 | OSGeoLive, so full WFS Response will be used as inputs values. As we will use the |
---|
332 | simple OGR Geometry functions like `OGR_G_GetBoundary <http://www.gdal.org/ogr/ogr__api_8h.html#a797af4266c02846d52b9cf3207ef958>`__, |
---|
333 | only the Geometry object will be used rather than a full WFS Response. The first |
---|
334 | thing to do is to write a function which will extract the geometry definition |
---|
335 | from the full WFS Response. We will call it createGeometryFromWFS. |
---|
336 | |
---|
337 | Here is the code of such a function: |
---|
338 | |
---|
339 | .. code-block:: guess |
---|
340 | |
---|
341 | OGRGeometryH createGeometryFromWFS(maps* conf,char* inputStr){ |
---|
342 | xmlInitParser(); |
---|
343 | xmlDocPtr doc = xmlParseMemory(inputStr,strlen(inputStr)); |
---|
344 | xmlChar *xmlbuff; |
---|
345 | int buffersize; |
---|
346 | xmlXPathContextPtr xpathCtx; |
---|
347 | xmlXPathObjectPtr xpathObj; |
---|
348 | char * xpathExpr="/*/*/*/*/*[local-name()='Polygon' or local-name()='MultiPolygon']"; |
---|
349 | xpathCtx = xmlXPathNewContext(doc); |
---|
350 | xpathObj = xmlXPathEvalExpression(BAD_CAST xpathExpr,xpathCtx); |
---|
351 | if(!xpathObj->nodesetval){ |
---|
352 | errorException(conf, "Unable to parse Input Polygon","InvalidParameterValue"); |
---|
353 | exit(0); |
---|
354 | } |
---|
355 | int size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0; |
---|
356 | xmlDocPtr ndoc = xmlNewDoc(BAD_CAST "1.0"); |
---|
357 | for(int k=size-1;k>=0;k--){ |
---|
358 | xmlDocSetRootElement(ndoc, xpathObj->nodesetval->nodeTab[k]); |
---|
359 | } |
---|
360 | xmlDocDumpFormatMemory(ndoc, &xmlbuff, &buffersize, 1); |
---|
361 | char *tmp=strdup(strstr((char*)xmlbuff,"?>")+2); |
---|
362 | xmlXPathFreeObject(xpathObj); |
---|
363 | xmlXPathFreeContext(xpathCtx); |
---|
364 | xmlFree(xmlbuff); |
---|
365 | xmlFreeDoc(doc); |
---|
366 | xmlCleanupParser(); |
---|
367 | OGRGeometryH res=OGR_G_CreateFromGML(tmp); |
---|
368 | if(res==NULL){ |
---|
369 | errorException(conf, "Unable to call OGR_G_CreatFromGML","NoApplicableCode"); |
---|
370 | exit(0); |
---|
371 | } |
---|
372 | else |
---|
373 | return res; |
---|
374 | } |
---|
375 | |
---|
376 | |
---|
377 | The only thing we will focus on is the call to the errorException function used |
---|
378 | in the function body. This function is declared in the zoo-kernel/service_internal.h |
---|
379 | and defined in zoo-kernel/service_internal.c file. It takes three parameters as follow: |
---|
380 | |
---|
381 | - the main environment maps, |
---|
382 | - a char* representing the error message to display, |
---|
383 | - a char* representing the error code (as defined in the WPS specification – Table 62). |
---|
384 | |
---|
385 | In other words, if the WFS response cannot be parsed properly, then you will return |
---|
386 | an ExceptionReport document informing the client that a problem occured. |
---|
387 | |
---|
388 | The function to extract the geometry object from a WFS Response is written, so you |
---|
389 | can now start defining the Boundary Service. Here is the full code for the Boundary Service: |
---|
390 | |
---|
391 | .. code-block:: guess |
---|
392 | |
---|
393 | int Boundary(maps*& conf,maps*& inputs,maps*& outputs){ |
---|
394 | OGRGeometryH geometry,res; |
---|
395 | map* tmp=getMapFromMaps(inputs,"InputPolygon","value"); |
---|
396 | if(tmp==NULL){ |
---|
397 | setMapInMaps(m,"lenv","message","Unable to parse InputPolygon"); |
---|
398 | return SERVICE_FAILED; |
---|
399 | } |
---|
400 | map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType"); |
---|
401 | if(strncmp(tmp1->value,"application/json",16)==0) |
---|
402 | geometry=OGR_G_CreateGeometryFromJson(tmp->value); |
---|
403 | else |
---|
404 | geometry=createGeometryFromWFS(conf,tmp->value); |
---|
405 | if(geometry==NULL){ |
---|
406 | setMapInMaps(m,"lenv","message","Unable to parse InputPolygon"); |
---|
407 | return SERVICE_FAILED; |
---|
408 | } |
---|
409 | res=OGR_G_GetBoundary(geometry); |
---|
410 | tmp1=getMapFromMaps(outputs,"Result","mimeType"); |
---|
411 | if(strncmp(tmp1->value,"application/json",16)==0){ |
---|
412 | char *tmp=OGR_G_ExportToJson(res); |
---|
413 | setMapInMaps(outputs,"Result","value",tmp); |
---|
414 | setMapInMaps(outputs,"Result","mimeType","text/plain"); |
---|
415 | free(tmp); |
---|
416 | } |
---|
417 | else{ |
---|
418 | char *tmp=OGR_G_ExportToGML(res); |
---|
419 | setMapInMaps(outputs,"Result","value",tmp); |
---|
420 | free(tmp); |
---|
421 | } |
---|
422 | outputs->next=NULL; |
---|
423 | OGR_G_DestroyGeometry(geometry); |
---|
424 | OGR_G_DestroyGeometry(res); |
---|
425 | return SERVICE_SUCCEEDED; |
---|
426 | } |
---|
427 | |
---|
428 | As you can see in the code above, the mimeType of the data inputs passed to our Service is first checked: |
---|
429 | |
---|
430 | .. code-block:: guess |
---|
431 | |
---|
432 | map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType"); |
---|
433 | if(strncmp(tmp1->value,"application/json",16)==0) |
---|
434 | geometry=OGR_G_CreateGeometryFromJson(tmp->value); |
---|
435 | else |
---|
436 | geometry=createGeometryFromWFS(conf,tmp->value); |
---|
437 | |
---|
438 | Basically, if we get an input with a mimeType set to application/json, then we will |
---|
439 | use our ``OGR_G_CreateGeometryFromJson`` in other case, our ``createGeometryFromWFS`` local function. |
---|
440 | |
---|
441 | Please note that in some sense the data inputs are not really of the same kind. |
---|
442 | Indeed as we used directly ``OGR_G_CreateGeometryFromJson`` it means that the JSON |
---|
443 | string include only the geometry object and not the full GeoJSON string. Nevertheless, |
---|
444 | you can easily change this code to be able to use a full GeoJSON string, simply by |
---|
445 | creating a function which will extract the geometry object from the GeoJSON string |
---|
446 | (using the json-c library for instance, which is also used by the OGR GeoJSON Driver). |
---|
447 | |
---|
448 | Once you can access the input geometry object, you can use the ``OGR_G_GetBoundary`` |
---|
449 | function and store the result in the res geometry variable. Then, you only have to |
---|
450 | store the value in the right format : GeoJSON per default or GML as we declared it as a supported output format. |
---|
451 | |
---|
452 | Please note that ZOO Kernel will give you pre-filled outputs values, so you will |
---|
453 | only have to fill the value for the key named value, even if in our example we |
---|
454 | override the mimeType using the text/plain value rather than the application/json |
---|
455 | (to show that we can also edit other fields of a map). Indeed, depending on the |
---|
456 | format requested by the client (or the default one) we will provide JSON or GML representation of the geometry. |
---|
457 | |
---|
458 | .. code-block:: guess |
---|
459 | |
---|
460 | tmp1=getMapFromMaps(outputs,"Result","mimeType"); |
---|
461 | if(strncmp(tmp1->value,"application/json",16)==0){ |
---|
462 | char *tmp=OGR_G_ExportToJson(res); |
---|
463 | setMapInMaps(outputs,"Result","value",tmp); |
---|
464 | setMapInMaps(outputs,"Result","mimeType","text/plain"); |
---|
465 | free(tmp); |
---|
466 | } |
---|
467 | else{ |
---|
468 | char *tmp=OGR_G_ExportToGML(res); |
---|
469 | setMapInMaps(outputs,"Result","value",tmp); |
---|
470 | free(tmp); |
---|
471 | } |
---|
472 | |
---|
473 | The Boundary ZOO Service is now implemented and you need to compile it to produce |
---|
474 | a Shared Library. As you just used functions defined in service.h (``getMapFromMaps``, |
---|
475 | ``setMapInMaps`` and ``addToMap``), you must include this file in your C code. The |
---|
476 | same requirement is needed to be able to use the ``errorException`` function declared |
---|
477 | in ``zoo-kernel/service_internal.h``, you also must link your service object file to |
---|
478 | the ``zoo-kernel/service_internal.o`` in order to use ``errorException`` on runtime. |
---|
479 | You must then include the required files to access the libxml2 and OGR C-API. |
---|
480 | |
---|
481 | For the need of the Shared Library, you have to put your code in a block declared as |
---|
482 | extern "C". The final Service code should be stored in a service.c file located in |
---|
483 | the root of the Services Provider directory (so in ``/home/zoows/sources/zoo-services/ws_sp``). |
---|
484 | It should look like this: |
---|
485 | |
---|
486 | .. code-block:: guess |
---|
487 | |
---|
488 | #include "ogr_api.h" |
---|
489 | #include "service.h" |
---|
490 | extern "C" { |
---|
491 | #include <libxml/tree.h> |
---|
492 | #include <libxml/parser.h> |
---|
493 | #include <libxml/xpath.h> |
---|
494 | #include <libxml/xpathInternals.h> |
---|
495 | <YOUR SERVICE CODE AND OTHER UTILITIES FUNCTIONS> |
---|
496 | } |
---|
497 | |
---|
498 | The full source code of your Service is now ready and you must produce the corresponding |
---|
499 | Service Shared Object by compiling the code as a Shared Library. This can be done using the following command: |
---|
500 | |
---|
501 | .. code-block:: guess |
---|
502 | |
---|
503 | g++ $CFLAGS -shared -fpic -o cgi-env/!ServicesProvider.zo ./service.c $LDFLAGS |
---|
504 | |
---|
505 | Please note that the ``CFLAGS`` and ``LDFLAGS`` environment variables values must be set before. |
---|
506 | |
---|
507 | The ``CFLAGS`` must contain all the requested paths to find included headers, so the |
---|
508 | path to the directories where the ``ogr_api.h``, ``libxml2`` directory, ``service.h`` |
---|
509 | and ``service_internal.h`` files are located. Thanks to the OSGeoLive environment, |
---|
510 | some of the provided tools can be used to retrieve those values : ``xml2-config`` and |
---|
511 | ``gdal-config``, both used with the ``--cflags`` argument. They will produce the desired paths for you. |
---|
512 | |
---|
513 | If you follow the instructions to create your ZOO Services Provider main directory in |
---|
514 | ``zoo-services``, then you should find the ZOO Kernel headers and source tree which is |
---|
515 | located in the ``../../zoo-kernel`` directory relatively to your current path (``/home/user/zoows/sources/zoo-services/ws_sp``). |
---|
516 | Note that you can also use a full path to the ``zoo-kernel`` directory but using relative |
---|
517 | path will let you move your sources tree somewhere else and keep your code compiling |
---|
518 | using exactly the same command line. So you must add a ``-I../../zoo-kernel`` to your |
---|
519 | ``CFLAGS`` to make the compiler able to find the ``service.h`` and ``service_internal.h`` files. |
---|
520 | |
---|
521 | The full ``CFLAGS`` definition should look like this: |
---|
522 | |
---|
523 | .. code-block:: guess |
---|
524 | |
---|
525 | CFLAGS=`gdal-config --cflags` `xml2-config --clfags` -I../../zoo-kernel/ |
---|
526 | |
---|
527 | Once you get the included paths correctly set in your ``CFLAGS`` , it is time to concentrate |
---|
528 | on the library we have to link against (defined in the ``LDFLAGS`` environment variable). |
---|
529 | In order to link against the gdal and libxml2 libraries, you can use the same tools than |
---|
530 | above using the ``--libs`` argument rather than ``--cflags``. The full ``LDFLAGS`` |
---|
531 | definition must look like this : |
---|
532 | |
---|
533 | .. code-block:: guess |
---|
534 | |
---|
535 | LDFLAGS=`gdal-config --libs` `xml2-config --libs` ../../zoo-kernel/service_internal.o |
---|
536 | |
---|
537 | Let's now create a ``Makefile`` which will help you compiling your code over the time. |
---|
538 | Please write a short ``Makefile`` in the root of your ZOO Services Provider directory, containing the following lines: |
---|
539 | |
---|
540 | .. code-block:: guess |
---|
541 | |
---|
542 | ZOO_SRC_ROOT=../../zoo-kernel/ |
---|
543 | CFLAGS=-I${ZOO_SRC_ROOT} `xml2-config --cflags` `gdal-config --cflags` |
---|
544 | LDFLAGS=`xml2-config --libs` `gdal-config --libs`${ZOO_SRC_ROOT}/service_internal.o |
---|
545 | |
---|
546 | cgi-env/ogr_ws_service_provider.zo: service.c |
---|
547 | g++ ${CFLAGS} -shared -fpic -o cgi-env/ogr_ws_service_provider.zo ./service.c $ {LDFLAGS} |
---|
548 | clean: |
---|
549 | rm -f cgi-env/ogr_ws_service_provider.zo |
---|
550 | |
---|
551 | |
---|
552 | Using this ``Makefile``, you should be able to run ``make`` from your ZOO Service Provider |
---|
553 | main directory and to get the resulting ``ogr_ws_service_provider.zo`` file located in the ``cgi-env`` directory. |
---|
554 | |
---|
555 | The metadata file and the ZOO Service Shared Object are now both located in the ``cgi-env`` |
---|
556 | directory. In order to deploy your new ServicesProvider, you only have to copy the ZOO |
---|
557 | Service Shared Object and its corresponding metadata file in the directory where ZOO |
---|
558 | Kernel is located, so in ``/usr/lib/cgi-bin``. You must use a ``sudo`` command to achieve this task: |
---|
559 | |
---|
560 | .. code-block:: guess |
---|
561 | |
---|
562 | sudo cp ./cgi-env/* /usr/lib/cgi-bin |
---|
563 | |
---|
564 | You should now understand more clearly the meannings of the ZOO Service Provider source tree ! |
---|
565 | The ``cgi-env`` directory will let you deploy your new Services or Services Provider in |
---|
566 | an easy way , simply by copying the whole cgi-env content in your ``cgi-bin`` directory. |
---|
567 | |
---|
568 | Please note that you can add the following lines to your ``Makefile`` to be able to type |
---|
569 | ``make install`` directly and to get your new Services Provider available for use from ZOO Kernel: |
---|
570 | |
---|
571 | .. code-block:: none |
---|
572 | |
---|
573 | install: |
---|
574 | sudo cp ./cgi-env/* /usr/lib/cgi-bin |
---|
575 | |
---|
576 | Your ZOO Services Provider is now ready to use from an Execute request passed to ZOO Kernel. |
---|
577 | |
---|
578 | Python Version |
---|
579 | ******************************************************************************************************* |
---|
580 | |
---|
581 | For those using Python to implement their ZOO Services Provider, the full code to copy in |
---|
582 | ``ogr_ws_service_provider.py`` in ``cgi-env`` directory is shown bellow. Indeed, as |
---|
583 | Python is an interpreted language, you do not have to compile anything before deploying |
---|
584 | your service which makes the deployement step much easier: |
---|
585 | |
---|
586 | .. code-block:: guess |
---|
587 | |
---|
588 | import osgeo.ogr |
---|
589 | import libxml2 |
---|
590 | |
---|
591 | def createGeometryFromWFS(my_wfs_response): |
---|
592 | doc=libxml2.parseMemory(my_wfs_response,len(my_wfs_response)) |
---|
593 | ctxt = doc.xpathNewContext() |
---|
594 | res=ctxt.xpathEval("/*/*/*/*/*[local-name()='Polygon' or local- name()='MultiPolygon']") |
---|
595 | for node in res: |
---|
596 | geometry_as_string=node.serialize() |
---|
597 | geometry=osgeo.ogr.CreateGeometryFromGML(geometry_as_string) |
---|
598 | return geometry |
---|
599 | return geometry |
---|
600 | |
---|
601 | def Boundary(conf,inputs,outputs): |
---|
602 | if inputs["InputPolygon"]["mimeType"]=="application/json": |
---|
603 | geometry=osgeo.ogr.CreateGeometryFromJson(inputs["InputPolygon"]["value"]) |
---|
604 | else: |
---|
605 | geometry=createGeometryFromWFS(inputs["InputPolygon"]["value"]) |
---|
606 | rgeom=geometry.GetBoundary() |
---|
607 | if outputs["Result"]["mimeType"]=="application/json": |
---|
608 | outputs["Result"]["value"]=rgeom.ExportToJson() |
---|
609 | outputs["Result"]["mimeType"]="text/plain" |
---|
610 | else: |
---|
611 | outputs["Result"]["value"]=rgeom.ExportToGML() |
---|
612 | geometry.Destroy() |
---|
613 | rgeom.Destroy() |
---|
614 | return 3 |
---|
615 | |
---|
616 | We do not dicuss the functions body here as we already gave all the details before and |
---|
617 | the code was volontary made in a similar way. |
---|
618 | |
---|
619 | As done before, you only have to copy the ``cgi-env`` files into your ``cgi-bin`` directory: |
---|
620 | |
---|
621 | .. code-block:: guess |
---|
622 | |
---|
623 | sudo cp ./cgi-env/* /usr/lib/cgi-bin |
---|
624 | |
---|
625 | A simple ``Makefile`` containing the install section can be written as the following : |
---|
626 | |
---|
627 | .. code-block:: none |
---|
628 | |
---|
629 | install: |
---|
630 | sudo cp ./cgi-env/* /usr/lib/cgi-bin/ |
---|
631 | |
---|
632 | Finally, simply run make install from the ZOO Services Provider main directory, in order to deploy your ZOO Service Provider. |
---|
633 | |
---|
634 | |
---|
635 | Testing the Service using Execute Request |
---|
636 | ............................................................................................................................................... |
---|
637 | |
---|
638 | **The simple and unreadable way** |
---|
639 | |
---|
640 | Everybody should now get his own copy of the OGR Boundary Service stored as a ZOO |
---|
641 | Services Provider called ``ogr_ws_service_provider`` and deployed in the ZOO Kernel |
---|
642 | tree, so the following Execute request can be used to test the Service: |
---|
643 | |
---|
644 | `link <http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192>`__ |
---|
645 | |
---|
646 | .. code-block:: guess |
---|
647 | |
---|
648 | http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192 |
---|
649 | |
---|
650 | As you can see in the url above, we use an URLEncoded WFS request to the MapServer |
---|
651 | WFS server available on OSGeoLive as a ``xlink:href`` key in the DataInputs KVP value, |
---|
652 | and set the ``InputPolygon`` value to Reference. The corresponding non encoded WFS request is as follow: |
---|
653 | |
---|
654 | :: |
---|
655 | |
---|
656 | http://localhost/cgi-bin/mapserv?map=/var/www/wfs.map&SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&typename=regions&SRS=EPSG:4326&featureid=regions.3192 |
---|
657 | |
---|
658 | Please note that you can add ``lineage=true`` to the previous request if you need |
---|
659 | to get information about the input values used to run your Service. Furthermore, |
---|
660 | you may need to store the ExecuteResponse document of your ZOO Service to re-use |
---|
661 | it later. In this case you must add ``storeExecuteResponse=true`` to the previous |
---|
662 | request. Note that is an important thing as the behavior of ZOO Kernel is not |
---|
663 | exactly the same than when running without this parameter settled to true. Indeed, |
---|
664 | in such a request, ZOO Kernel will give you an ExecuteResponse document which will |
---|
665 | contain the attribute statusLocation, which inform the client where the ongoing |
---|
666 | status or the final ExecuteResponse will be located. |
---|
667 | |
---|
668 | Here is an example of what the ExecuteResponse would look like in case ``storeExecuteResponse`` was set to true in the request: |
---|
669 | |
---|
670 | .. image:: ./images/Practical-introduction-to-ZOO-7.png |
---|
671 | :width: 610px |
---|
672 | :height: 146px |
---|
673 | :align: center |
---|
674 | |
---|
675 | Then, according to the statusLocation, you should get the ExecuteResponse as you get |
---|
676 | before using the previous request. Note that can be really useful to provide some |
---|
677 | caching system for a client application. |
---|
678 | |
---|
679 | You didn't specify any ResponseForm in the previous request, it is not requested |
---|
680 | and should return a ResponseDocument per default using the application/json mimeType |
---|
681 | as you defined in you zcfg file. Nevertheless, you can tell ZOO Kernel what kind of |
---|
682 | data you want to get in result of your query adding the attribute ``mimeType=text/xml`` |
---|
683 | to your ``ResponseDocument`` parameter. Adding this parameter to the previous request |
---|
684 | will give us the result as its GML representation : |
---|
685 | |
---|
686 | `link <http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192&ResponseDocument=Result@mimeType=text/xml>`__ |
---|
687 | |
---|
688 | .. code-block:: guess |
---|
689 | |
---|
690 | http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192&ResponseDocument=Result@mimeType=text/xml |
---|
691 | |
---|
692 | As defined by the WPS specifications, you can also ask for a ``RawDataOutput`` to |
---|
693 | get only the data without the full ``ResponseDocument``. To do that, you only have |
---|
694 | to replace the ``ResponseDocument`` of your request by ``RawDataOutput``, like in |
---|
695 | the following request : |
---|
696 | |
---|
697 | `link <http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192&RawDataOutput=Result@mimeType=application/json>`__ |
---|
698 | |
---|
699 | .. code-block:: guess |
---|
700 | |
---|
701 | http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192&RawDataOutput=Result@mimeType=application/json |
---|
702 | |
---|
703 | Please note that we go back to the default mimeType to directly obtain the JSON |
---|
704 | string as we will use this kind of request to develop our client application in |
---|
705 | the next section of this workshop. |
---|
706 | |
---|
707 | Now, you know how to ask ZOO Kernel to run service in background, ask for ``RawDataOutput`` |
---|
708 | specifying ``mimeType`` or any specific format to be returned by the Kernel. When you |
---|
709 | ask for ``ResponseDocument``, you can also specify to the ZOO Kernel that you want the |
---|
710 | result to be stored on the server side. |
---|
711 | |
---|
712 | To do such a thing, you have to set the attribute ``asReference`` as true and then the |
---|
713 | resulting ExecuteResponse will contain a Reference node including the href attribute |
---|
714 | to let you access the produced file. To be able to handle this, you have to add the |
---|
715 | extension parameter in your ``DataOutputs`` node in the corresponding ZCFG file. |
---|
716 | |
---|
717 | Here is a sample url which provide such a result: |
---|
718 | |
---|
719 | `link <http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192&ResponseDocument=Result@mimeType=application/json@asReference=true>`__ |
---|
720 | |
---|
721 | .. code-block:: guess |
---|
722 | |
---|
723 | http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fvar%2Fwww%2Fwfs.map%26SERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192&ResponseDocument=Result@mimeType=application/json@asReference=true |
---|
724 | |
---|
725 | You can see bellow what kind of result can be expected : |
---|
726 | |
---|
727 | .. image:: ./images/screenshot-ZOO-asReference-attribute.png |
---|
728 | :width: 620px |
---|
729 | :height: 217px |
---|
730 | :align: center |
---|
731 | |
---|
732 | **Simplification and readability of request** |
---|
733 | |
---|
734 | As you can see in the simple example we used since the begining of this workshop, |
---|
735 | it is sometimes hard to write the Execute requests using the GET method as it |
---|
736 | makes really long and complexe URLs. In the next requests examples, we will |
---|
737 | thus use the POST XML requests. First , here is the XML request corresponding |
---|
738 | to the previous Execute we used: |
---|
739 | |
---|
740 | .. code-block:: guess |
---|
741 | |
---|
742 | <wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 ../wpsExecute_request.xsd"> |
---|
743 | <ows:Identifier>Boundary</ows:Identifier> |
---|
744 | <wps:DataInputs> |
---|
745 | <wps:Input> |
---|
746 | <ows:Identifier>InputPolygon</ows:Identifier> |
---|
747 | <ows:Title>Playground area</ows:Title> |
---|
748 | <wps:Reference xlink:href="http://localhost/cgi-bin/mapserv?map=/var/www/wfs.map&SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&typename=regions&SRS=EPSG:4326&featureid=regions.3192"/> |
---|
749 | </wps:Input> |
---|
750 | </wps:DataInputs> |
---|
751 | <wps:ResponseForm> |
---|
752 | <wps:ResponseDocument> |
---|
753 | <wps:Output> |
---|
754 | <ows:Identifier>Result</ows:Identifier> |
---|
755 | <ows:Title>Area serviced by playground.</ows:Title> |
---|
756 | <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract> |
---|
757 | </wps:Output> |
---|
758 | </wps:ResponseDocument> |
---|
759 | </wps:ResponseForm> |
---|
760 | </wps:Execute> |
---|
761 | |
---|
762 | In order to let you easily run the XML requests, a simple HTML form called |
---|
763 | ``test_services.html`` is available in your ``/var/www`` directory. You can |
---|
764 | access it using the following link : http://localhost/test_services.html. |
---|
765 | |
---|
766 | Please open this page in your browser, simply fill the XML request content into |
---|
767 | the textarea field and click the « run using XML Request » submit button. You will |
---|
768 | get exactly the same result as when running your Service using the GET request. The |
---|
769 | screenshot above show the HTML form including the request and the ExecuteResponse |
---|
770 | document displayed in the iframe at the bottom of the page: |
---|
771 | |
---|
772 | .. image:: ./images/Practical-introduction-to-ZOO-8.png |
---|
773 | :width: 573px |
---|
774 | :height: 308px |
---|
775 | :align: center |
---|
776 | |
---|
777 | The xlink:href value is used in the simplest way to deal with such data input. Obviously, |
---|
778 | you can also use a full JSON string of the geometry, as shown in the following XML Request example : |
---|
779 | |
---|
780 | .. code-block:: guess |
---|
781 | |
---|
782 | <wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 ../wpsExecute_request.xsda"> |
---|
783 | <ows:Identifier>Boundary</ows:Identifier> |
---|
784 | <wps:DataInputs> |
---|
785 | <wps:Input> |
---|
786 | <ows:Identifier>InputPolygon</ows:Identifier> |
---|
787 | <wps:Data> |
---|
788 | <wps:ComplexData mimeType="application/json"> |
---|
789 | { "type": "MultiPolygon", "coordinates": [ [ [ [ -105.998360, 31.393818 ], [ -106.212753, 31.478128 ], [ -106.383041, 31.733763 ], [ -106.538971, 31.786198 ], [ -106.614441, 31.817728 ], [ -105.769730, 31.170780 ], [ -105.998360, 31.393818 ] ] ], [ [ [ -94.913429, 29.257572 ], [ -94.767380, 29.342451 ], [ -94.748405, 29.319490 ], [ -95.105415, 29.096958 ], [ -94.913429, 29.257572 ] ] ] ] } |
---|
790 | </wps:ComplexData> |
---|
791 | </wps:Data> |
---|
792 | </wps:Input> |
---|
793 | </wps:DataInputs> |
---|
794 | <wps:ResponseForm> |
---|
795 | <wps:ResponseDocument> |
---|
796 | <wps:Output> |
---|
797 | <ows:Identifier>Result</ows:Identifier> |
---|
798 | <ows:Title>Area serviced by playground.</ows:Title> |
---|
799 | <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract> |
---|
800 | </wps:Output> |
---|
801 | </wps:ResponseDocument> |
---|
802 | </wps:ResponseForm> |
---|
803 | </wps:Execute> |
---|
804 | |
---|
805 | If everything went well, you should get the Boundary of the JSON geometry passed as |
---|
806 | argument, and so be sure that your Service support both GML and JSON as input data. |
---|
807 | Note that in the previous request, we added a ``mimeType`` attribute to the |
---|
808 | ``ComplexData`` node to specify that the input data is not in the default ``text/xml`` |
---|
809 | mimeType but passed as an ``application/json`` string directly. It is similar to add |
---|
810 | ``@mimeType=application/json`` as we discussed before. |
---|
811 | |
---|
812 | **storeExecuteResponse parameter and GetStatus Service** |
---|
813 | |
---|
814 | If you go in your local ``/home/user/zoows/sources/zoo-services/utils/status``, you'll |
---|
815 | find the code for a ServiceProvider which will provide the GetStatus service and the |
---|
816 | longProcess one. The last is a simple example to learn how to use the status variable |
---|
817 | from lenv section of the main configuration maps and the updateStatus function you |
---|
818 | have to call to take your status value into account. The main service provider is |
---|
819 | the GetStatus one, it is able to give you information about the current status value |
---|
820 | from a service running in background mode. |
---|
821 | |
---|
822 | You have to know that the ZOO Kernel will detect the presence of the GetStatus service |
---|
823 | and if it is available it will then return the link the corresponding Execute request. |
---|
824 | |
---|
825 | So now you will deploy the GetStatus and longProcess service on your local environment. |
---|
826 | As for each services, you shall be able to deploy the services simply by copying the |
---|
827 | cgi-env directory into your Apache ``cgi-bin`` directory. You can use the following command : |
---|
828 | |
---|
829 | .. code-block:: guess |
---|
830 | |
---|
831 | sudo cp ~user/zoows/sources/zoo-services/utils/status/cgi-env/*{zcfg,zo} /usr/lib/cgi-bin |
---|
832 | |
---|
833 | For simple Services it is the right way to deploy Service Providers. But in this specific |
---|
834 | case you'll have also to add some special parameter in the main section of you main |
---|
835 | configuration file and to copy an xsl file used to replace on the fly in the ResponseDocument |
---|
836 | the percentCompleted attribute of the ProcessStarted node returned by the GetStatus service. |
---|
837 | |
---|
838 | So first edit you ``main.cfg`` file to add the following lines in your main section : |
---|
839 | |
---|
840 | .. code-block:: guess |
---|
841 | |
---|
842 | rewriteUrl=call |
---|
843 | dataPath=/var/www/data |
---|
844 | |
---|
845 | Here you define the path where the service is able to find the xsl file, specified in the |
---|
846 | dataPath parameter. You also tell the ZOO Kernel that you want to use the rewriteUrl we |
---|
847 | defined in the previous section. |
---|
848 | |
---|
849 | To finish your deployment, you'll have now to copy the xsl file in the defined dataPath |
---|
850 | directory. You can use the following command : |
---|
851 | |
---|
852 | .. code-block:: guess |
---|
853 | |
---|
854 | cp ~/zoows/sources/zoo-services/utils/status/cgi-env/*xsl /var/www/data |
---|
855 | |
---|
856 | Now, if you run the following request to run the service longProcess : |
---|
857 | |
---|
858 | http://localhost/zoo/?request=Execute&service=WPS&version=1.0.0&Identifier=longProcess&DataInputs=&storeExecuteResponse=true |
---|
859 | |
---|
860 | You shall get the a XML document looking like the following: |
---|
861 | |
---|
862 | .. image:: ./images/Practical-introduction-to-ZOO-9.png |
---|
863 | :width: 590px |
---|
864 | :height: 155px |
---|
865 | :align: center |
---|
866 | |
---|
867 | If you poll the statusLocation url provider in the answer you'll then be able to view |
---|
868 | the evolution of the percentCompleted attribute value growing, like you can see in the following screenshot. |
---|
869 | |
---|
870 | .. image:: ./images/Practical-introduction-to-ZOO-10.png |
---|
871 | :width: 589px |
---|
872 | :height: 146px |
---|
873 | :align: center |
---|
874 | |
---|
875 | This won't be used during this workshop but can be useful for really time consuming services. |
---|
876 | |
---|
877 | |
---|
878 | Creating Services for other functions (ConvexHull and Centroid) |
---|
879 | ......................................................................................................................................................... |
---|
880 | |
---|
881 | |
---|
882 | As the Boundary sample service code is available, you can now easily add ConvexHull and |
---|
883 | Centroid functions as they take exactly the same number of arguments : Only one geometry. |
---|
884 | The details for implementing and deploying the ConvexHull Service are provided bellow, |
---|
885 | and we will let you do the same thing for the Centroid one. |
---|
886 | |
---|
887 | C Version |
---|
888 | ******************************************************************************************************* |
---|
889 | Please add first the following code to the service.c source code : |
---|
890 | |
---|
891 | .. code-block:: guess |
---|
892 | |
---|
893 | int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){ |
---|
894 | OGRGeometryH geometry,res; |
---|
895 | map* tmp=getMapFromMaps(inputs,"InputPolygon","value"); |
---|
896 | if(tmp==NULL){ |
---|
897 | setMapInMaps(conf,"lenv","message","Unable to fetch InputPolygon value."); |
---|
898 | return SERVICE_FAILED; |
---|
899 | } |
---|
900 | map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType"); |
---|
901 | if(strncmp(tmp1->value,"application/json",16)==0) |
---|
902 | geometry=OGR_G_CreateGeometryFromJson(tmp->value); |
---|
903 | else |
---|
904 | geometry=createGeometryFromWFS(conf,tmp->value); |
---|
905 | if(geometry==NULL){ |
---|
906 | setMapInMaps(conf,"lenv","message","Unable to parse InputPolygon value."); |
---|
907 | return SERVICE_FAILED; |
---|
908 | } |
---|
909 | res=OGR_G_ConvexHull(geometry); |
---|
910 | tmp1=getMapFromMaps(outputs,"Result","mimeType"); |
---|
911 | if(strncmp(tmp1->value,"application/json",16)==0){ |
---|
912 | char* tmp=OGR_G_ExportToJson(res); |
---|
913 | setMapInMaps(outputs,"Result","value",tmp); |
---|
914 | setMapInMaps(outputs,"Result","mimeType","text/plain"); |
---|
915 | free(tmp); |
---|
916 | } |
---|
917 | else{ |
---|
918 | char* tmp=OGR_G_ExportToGML(res); |
---|
919 | setMapInMaps(outputs,"Result","value",tmp); |
---|
920 | free(tmp); |
---|
921 | } |
---|
922 | OGR_G_DestroyGeometry(geometry); |
---|
923 | OGR_G_DestroyGeometry(res); |
---|
924 | return SERVICE_SUCCEEDED; |
---|
925 | } |
---|
926 | |
---|
927 | |
---|
928 | This new code is exactly the same as for the Boundary Service. The only thing we modified |
---|
929 | is the line where the `OGR_G_ConvexHull <http://www.gdal.org/ogr/ogr__api_8h.html#7a93026cfae8ee6ce25546dba1b2df7d>`__ |
---|
930 | function is called (rather than the OGR_G_GetBoundary you used before). It is better to not copy |
---|
931 | and paste the whole function and find a more generic way to define your new Services as the |
---|
932 | function body will be the same in every case. The following generic function is proposed to make things simpler: |
---|
933 | |
---|
934 | .. code-block:: guess |
---|
935 | |
---|
936 | int applyOne(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc) (OGRGeometryH)){ |
---|
937 | OGRGeometryH geometry,res; |
---|
938 | map* tmp=getMapFromMaps(inputs,"InputPolygon","value"); |
---|
939 | if(tmp==NULL){ |
---|
940 | setMapInMaps(conf,"lenv","message","Unable to fetch InputPolygon value."); |
---|
941 | return SERVICE_FAILED; |
---|
942 | } |
---|
943 | map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType"); |
---|
944 | if(strncmp(tmp1->value,"application/json",16)==0) |
---|
945 | geometry=OGR_G_CreateGeometryFromJson(tmp->value); |
---|
946 | else |
---|
947 | geometry=createGeometryFromWFS(conf,tmp->value); |
---|
948 | if(geometry==NULL){ |
---|
949 | setMapInMaps(conf,"lenv","message","Unable to parse InputPolygon value."); |
---|
950 | return SERVICE_FAILED; |
---|
951 | } |
---|
952 | res=(*myFunc)(geometry); |
---|
953 | tmp1=getMapFromMaps(outputs,"Result","mimeType"); |
---|
954 | if(strncmp(tmp1->value,"application/json",16)==0){ |
---|
955 | char *tmp=OGR_G_ExportToJson(res); |
---|
956 | setMapInMaps(outputs,"Result","value",tmp); |
---|
957 | setMapInMaps(outputs,"Result","mimeType","text/plain"); |
---|
958 | free(tmp); |
---|
959 | } |
---|
960 | else{ |
---|
961 | char *tmp=OGR_G_ExportToGML(res); |
---|
962 | setMapInMaps(outputs,"Result","value",tmp); |
---|
963 | free(tmp); |
---|
964 | } |
---|
965 | outputs->next=NULL; |
---|
966 | OGR_G_DestroyGeometry(geometry); |
---|
967 | OGR_G_DestroyGeometry(res); |
---|
968 | return SERVICE_SUCCEEDED; |
---|
969 | } |
---|
970 | |
---|
971 | Then, a function pointer called myFunc rather than the full function name can be used. |
---|
972 | This way we can re-implement our Boundary Service this way: |
---|
973 | |
---|
974 | .. code-block:: guess |
---|
975 | |
---|
976 | int Boundary(maps*& conf,maps*& inputs,maps*& outputs){ |
---|
977 | return applyOne(conf,inputs,outputs,&OGR_G_GetBoundary); |
---|
978 | } |
---|
979 | |
---|
980 | Using this applyOne local function defined in the service.c source code, we can define |
---|
981 | other Services this way: |
---|
982 | |
---|
983 | .. code-block:: guess |
---|
984 | |
---|
985 | int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){ |
---|
986 | return applyOne(conf,inputs,outputs,&OGR_G_ConvexHull); |
---|
987 | } |
---|
988 | int Centroid(maps*& conf,maps*& inputs,maps*& outputs){ |
---|
989 | return applyOne(conf,inputs,outputs,&MY_OGR_G_Centroid); |
---|
990 | } |
---|
991 | |
---|
992 | The genericity of the applyOne function let you add two new Services in your ZOO Services Provider : ConvexHull and Centroid. |
---|
993 | |
---|
994 | Note that you should define MY_OGR_Centroid function before the Centroid one as `OGR_G_Centroid <http://www.gdal.org/ogr/ogr__api_8h.html#23f5a19a81628af7f9cc59a37378cb2b>`__ don't return a geometry object but set the value to an already existing one and support only Polygon as input, so to ensure we use the ConvexHull for MultiPolygon. So please use the code bellow: |
---|
995 | |
---|
996 | .. code-block:: guess |
---|
997 | |
---|
998 | OGRGeometryH MY_OGR_G_Centroid(OGRGeometryH hTarget){ |
---|
999 | OGRGeometryH res; |
---|
1000 | res=OGR_G_CreateGeometryFromJson("{\"type\": \"Point\", \"coordinates\": [0,0] }"); |
---|
1001 | OGRwkbGeometryType gtype=OGR_G_GetGeometryType(hTarget); |
---|
1002 | if(gtype!=wkbPolygon){ |
---|
1003 | hTarget=OGR_G_ConvexHull(hTarget); |
---|
1004 | } |
---|
1005 | OGR_G_Centroid(hTarget,res); |
---|
1006 | return res; |
---|
1007 | } |
---|
1008 | |
---|
1009 | To deploy your Services, you only have to copy the ``Boundary.zcfg`` metadata file from |
---|
1010 | your cgi-env directory as ``ConvexHull.zcfg`` and ``Centroid.zcfg``. Then, you must |
---|
1011 | rename the Service name on the first line to be able to run and test the Execute request |
---|
1012 | in the same way you did before. You only have to set the Identifier value to ConvexHull |
---|
1013 | or Centroid in your request depending on the Service you want to run. |
---|
1014 | |
---|
1015 | Note here that the GetCapabilities and DescribeProcess requests will return odd results |
---|
1016 | as we didn't modified any metadata informations, you can edit the ``.zcfg`` files to set |
---|
1017 | correct values. By the way it can be used for testing purpose, as the input and output |
---|
1018 | get the same name and default/supported formats. |
---|
1019 | |
---|
1020 | Python Version |
---|
1021 | ******************************************************************************************************* |
---|
1022 | |
---|
1023 | .. code-block:: guess |
---|
1024 | |
---|
1025 | def ConvexHull(conf,inputs,outputs): |
---|
1026 | if inputs["InputPolygon"]["mimeType"]=="application/json": |
---|
1027 | geometry=osgeo.ogr.CreateGeometryFromJson(inputs["InputPolygon"]["value"]) |
---|
1028 | else: |
---|
1029 | geometry=createGeometryFromWFS(inputs["InputPolygon"]["value"]) |
---|
1030 | rgeom=geometry.ConvexHull() |
---|
1031 | if outputs["Result"]["mimeType"]=="application/json": |
---|
1032 | outputs["Result"]["value"]=rgeom.ExportToJson() |
---|
1033 | outputs["Result"]["mimeType"]="text/plain" |
---|
1034 | else: |
---|
1035 | outputs["Result"]["value"]=rgeom.ExportToGML() |
---|
1036 | geometry.Destroy() |
---|
1037 | rgeom.Destroy() |
---|
1038 | return 3 |
---|
1039 | |
---|
1040 | |
---|
1041 | Once again, you can easily copy and paste the function for Boundary and simply modify |
---|
1042 | the line where the Geometry method was called. Nevertheless, as we did for the C language |
---|
1043 | we will give you a simple way to get things more generic. |
---|
1044 | |
---|
1045 | First of all, the first step which consists in extracting the InputPolygon Geometry as |
---|
1046 | it will be used in the same way in each Service functions, so we will first create a |
---|
1047 | function which will do that for us. The same thing can also be done for filling the |
---|
1048 | output value, so we will define another function to do that automaticaly. Here is the |
---|
1049 | code of this two functions (extractInputs and outputResult) : |
---|
1050 | |
---|
1051 | .. code-block:: guess |
---|
1052 | |
---|
1053 | def extractInputs(obj): |
---|
1054 | if obj["mimeType"]=="application/json": |
---|
1055 | return osgeo.ogr.CreateGeometryFromJson(obj["value"]) |
---|
1056 | else: |
---|
1057 | return createGeometryFromWFS(obj["value"]) |
---|
1058 | return null |
---|
1059 | |
---|
1060 | def outputResult(obj,geom): |
---|
1061 | if obj["mimeType"]=="application/json": |
---|
1062 | obj["value"]=geom.ExportToJson() |
---|
1063 | obj["mimeType"]="text/plain" |
---|
1064 | else: |
---|
1065 | obj["value"]=geom.ExportToGML() |
---|
1066 | |
---|
1067 | We can so minimize the code of the Boundary function to make it simplier using the following function definition : |
---|
1068 | |
---|
1069 | .. code-block:: guess |
---|
1070 | |
---|
1071 | def Boundary(conf,inputs,outputs): |
---|
1072 | geometry=extractInputs(inputs["InputPolygon"]) |
---|
1073 | rgeom=geometry.GetBoundary() |
---|
1074 | outputResult(outputs["Result"],rgeom) |
---|
1075 | geometry.Destroy() |
---|
1076 | rgeom.Destroy() |
---|
1077 | return 3 |
---|
1078 | |
---|
1079 | Then definition of the ConvexHull and Centroid Services can be achieved using the following code: |
---|
1080 | |
---|
1081 | .. code-block:: guess |
---|
1082 | |
---|
1083 | def ConvexHull(conf,inputs,outputs): |
---|
1084 | geometry=extractInputs(inputs["InputPolygon"]) |
---|
1085 | rgeom=geometry.ConvexHull() |
---|
1086 | outputResult(outputs["Result"],rgeom) |
---|
1087 | geometry.Destroy() |
---|
1088 | rgeom.Destroy() |
---|
1089 | return 3 |
---|
1090 | |
---|
1091 | def Centroid(conf,inputs,outputs): |
---|
1092 | geometry=extractInputs(inputs["InputPolygon"]) |
---|
1093 | if geometry.GetGeometryType()!=3: |
---|
1094 | geometry=geometry.ConvexHull() |
---|
1095 | rgeom=geometry.Centroid() |
---|
1096 | outputResult(outputs["Result"],rgeom) |
---|
1097 | geometry.Destroy() |
---|
1098 | rgeom.Destroy() |
---|
1099 | return 3 |
---|
1100 | |
---|
1101 | Note, that in Python you also need to use ConvexHull to deal with MultiPolygons. |
---|
1102 | |
---|
1103 | You must now copy the ``Boundary.zcfg`` file as we explained for the C version in ``ConvexHull.zcfg`` and ``Centroid.zcfg`` respectively and then, use make install command to re-deploy and test your Services Provider. |
---|
1104 | |
---|
1105 | Create the Buffer Service |
---|
1106 | |
---|
1107 | |
---|
1108 | We can now work on the Buffer Service, which takes more arguments than the other ones. |
---|
1109 | Indeed, the code is a bit different from the one used to implement the Boundary, ConvexHull and Centroid Services. |
---|
1110 | |
---|
1111 | The Buffer service also takes an input geometry, but uses a BufferDistance parameter. |
---|
1112 | It will also allow you to define LitteralData block as the BufferDistance will be |
---|
1113 | simple integer value. The read access to such kind of input value is made using the |
---|
1114 | same function as used before. |
---|
1115 | |
---|
1116 | C Version |
---|
1117 | ******************************************************************************************************* |
---|
1118 | |
---|
1119 | If you go back to the first Boundary Service source code, you should not find the |
---|
1120 | following very complicated. Indeed, you simply have to add the access of the |
---|
1121 | BufferDistance argument and modify the line whenthe `OGR_G_Buffer <http://www.gdal.org/ogr/ogr__api_8h.html#1ca0bd5c0fcb4b1af3c3973e467b0ec0>`__ |
---|
1122 | must be called (instead of OGR_G_GetBoundary). Here is the ful lcode : |
---|
1123 | |
---|
1124 | .. code-block:: guess |
---|
1125 | |
---|
1126 | int Buffer(maps*& conf,maps*& inputs,maps*& outputs){ |
---|
1127 | OGRGeometryH geometry,res; |
---|
1128 | map* tmp1=getMapFromMaps(inputs,"InputPolygon","value"); |
---|
1129 | if(tmp==NULL){ |
---|
1130 | setMapInMaps(conf,"lenv","message","Unable to fetch InputPolygon value."); |
---|
1131 | return SERVICE_FAILED; |
---|
1132 | } |
---|
1133 | map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType"); |
---|
1134 | if(strncmp(tmp->value,"application/json",16)==0) |
---|
1135 | geometry=OGR_G_CreateGeometryFromJson(tmp->value); |
---|
1136 | else |
---|
1137 | geometry=createGeometryFromWFS(conf,tmp->value); |
---|
1138 | double bufferDistance=1; |
---|
1139 | tmp=getMapFromMaps(inputs,"BufferDistance","value"); |
---|
1140 | if(tmp!=NULL) |
---|
1141 | bufferDistance=atof(tmp->value); |
---|
1142 | res=OGR_G_Buffer(geometry,bufferDistance,30); |
---|
1143 | tmp1=getMapFromMaps(outputs,"Result","mimeType"); |
---|
1144 | if(strncmp(tmp1->value,"application/json",16)==0){ |
---|
1145 | char *tmp=OGR_G_ExportToJson(res); |
---|
1146 | setMapInMaps(outputs,"Result","value",tmp); |
---|
1147 | setMapInMaps(outputs,"Result","mimeType","text/plain"); |
---|
1148 | free(tmp); |
---|
1149 | } |
---|
1150 | else{ |
---|
1151 | char *tmp=OGR_G_ExportToGML(res); |
---|
1152 | setMapInMaps(outputs,"Result","value",tmp); |
---|
1153 | free(tmp); |
---|
1154 | } |
---|
1155 | outputs->next=NULL; |
---|
1156 | OGR_G_DestroyGeometry(geometry); |
---|
1157 | OGR_G_DestroyGeometry(res); |
---|
1158 | return SERVICE_SUCCEEDED; |
---|
1159 | } |
---|
1160 | |
---|
1161 | The new code must be inserted in your service.c file and need to be recompiled and |
---|
1162 | replace the older version of your ZOO Service Provider in the /usr/lib/cgi-bin/ directory. |
---|
1163 | You must of course place the corresponding ZOO Metadata File in the same directory. |
---|
1164 | |
---|
1165 | As we explained before, ZOO Kernel is permissive in the sense that you can pass more |
---|
1166 | arguments than defined in you zcfg file, so let's try using a copy of the ``Boundary.zcfg`` |
---|
1167 | file renamed as ``Buffer.zcfg`` and containing the Buffer identifier. Then, please |
---|
1168 | test your service using an Execute request as you did before. You will obtain the |
---|
1169 | buffer result in a ResponseDocument. |
---|
1170 | |
---|
1171 | You may have noted that the above code check if a BufferDistance input was passed |
---|
1172 | to the service. If not, we will use 1 as the default value, which explains why |
---|
1173 | you do not have to use one more input to your previous queries. |
---|
1174 | |
---|
1175 | You can change the BufferDistance value used by your Service to compute Buffer |
---|
1176 | of your geometry by adding it to the DataInputs value in your request. Note that |
---|
1177 | using KVP syntaxe, each DataInputs are separated by a semicolon. |
---|
1178 | |
---|
1179 | So, the previous request: |
---|
1180 | |
---|
1181 | .. code-block:: guess |
---|
1182 | |
---|
1183 | DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3FSERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192 |
---|
1184 | |
---|
1185 | Can now be rewritten this way : |
---|
1186 | |
---|
1187 | .. code-block:: guess |
---|
1188 | |
---|
1189 | DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%2Fcgi-bin%2Fmapserv%3FSERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dregions%26SRS%3DEPSG%3A4326%26FeatureID%3Dregions.3192;BufferDistance=2 |
---|
1190 | |
---|
1191 | Setting BufferDistance value to 2 would give you a different result, then don't |
---|
1192 | pass any other parameter as we defined 1 as the default value in the source code. |
---|
1193 | |
---|
1194 | Here you can find the same query in XML format to use from the http://localhost/test_services.html HTML form : |
---|
1195 | |
---|
1196 | .. code-block:: guess |
---|
1197 | |
---|
1198 | <wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 ../wpsExecute_request.xsda"> |
---|
1199 | <ows:Identifier>Buffer</ows:Identifier> |
---|
1200 | <wps:DataInputs> |
---|
1201 | <wps:Input> |
---|
1202 | <ows:Identifier>InputPolygon</ows:Identifier> |
---|
1203 | <ows:Title>Playground area</ows:Title> |
---|
1204 | <wps:Reference xlink:href="http://localhost/cgi-bin/mapserv?map=/var/www/wfs.map&SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&typename=regions&SRS=EPSG:4326&featureid=regions.3192"/> |
---|
1205 | </wps:Input> |
---|
1206 | <wps:Input> |
---|
1207 | <ows:Identifier>BufferDistance</ows:Identifier> |
---|
1208 | <wps:Data> |
---|
1209 | <wps:LiteralData uom="degree">2</wps:LiteralData> |
---|
1210 | </wps:Data> |
---|
1211 | </wps:Input> |
---|
1212 | </wps:DataInputs> |
---|
1213 | <wps:ResponseForm> |
---|
1214 | <wps:ResponseDocument> |
---|
1215 | <wps:Output> |
---|
1216 | <ows:Identifier>Buffer</ows:Identifier> |
---|
1217 | <ows:Title>Area serviced by playground.</ows:Title> |
---|
1218 | <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract> |
---|
1219 | </wps:Output> |
---|
1220 | </wps:ResponseDocument> |
---|
1221 | </wps:ResponseForm> |
---|
1222 | </wps:Execute> |
---|
1223 | |
---|
1224 | Python Version |
---|
1225 | ******************************************************************************************************* |
---|
1226 | |
---|
1227 | As we already defined the utility functions createGeometryFromWFS and outputResult, |
---|
1228 | the code is as simple as this: |
---|
1229 | |
---|
1230 | .. code-block:: guess |
---|
1231 | |
---|
1232 | def Buffer(conf,inputs,outputs): |
---|
1233 | geometry=extractInputs(inputs["InputPolygon"]) |
---|
1234 | try: |
---|
1235 | bdist=int(inputs["BufferDistance"]["value"]) |
---|
1236 | except: |
---|
1237 | bdist=10 |
---|
1238 | rgeom=geometry.Buffer(bdist) |
---|
1239 | outputResult(outputs["Result"],rgeom) |
---|
1240 | geometry.Destroy() |
---|
1241 | rgeom.Destroy() |
---|
1242 | return 3 |
---|
1243 | |
---|
1244 | We simply added the use of inputs["BufferDistance"]["value"] as arguments of the |
---|
1245 | Geometry instance Buffer method. Once you get this code added to your ogr_ws_service_provider.py |
---|
1246 | file, simply copy it in the ZOO Kernel directory (or type make install from your ZOO Service |
---|
1247 | Provider root directory). Note that you also need the ``Buffer.zcfg`` file detailled in the next section. |
---|
1248 | |
---|
1249 | The Buffer MetadataFile file |
---|
1250 | ******************************************************************************************************* |
---|
1251 | |
---|
1252 | You must add BufferDistance to the Service Metadata File to let clients know that |
---|
1253 | this Service supports this parameter. To do this, please copy your orginal ``Boundary.zcfg`` |
---|
1254 | file as ``Buffer.zcfg`` and add the following lines to the DataInputs block : |
---|
1255 | |
---|
1256 | .. code-block:: none |
---|
1257 | |
---|
1258 | [BufferDistance] |
---|
1259 | Title = Buffer Distance |
---|
1260 | Abstract = Distance to be used to calculate buffer. |
---|
1261 | minOccurs = 0 |
---|
1262 | maxOccurs = 1 |
---|
1263 | <LiteralData> |
---|
1264 | DataType = float |
---|
1265 | <Default> |
---|
1266 | uom = degree |
---|
1267 | value = 10 |
---|
1268 | </Default> |
---|
1269 | <Supported> |
---|
1270 | uom = meter |
---|
1271 | </Supported> |
---|
1272 | </LiteralData> |
---|
1273 | |
---|
1274 | Note that as minOccurs is set to 0 which means that the input parameter is optional |
---|
1275 | and don't have to be passed. You must know that ZOO Kernel will pass the default |
---|
1276 | value to the Service function for an optional parameter with a default value set. |
---|
1277 | |
---|
1278 | You can get a full copy of the ``Buffer.zcfg`` file here : |
---|
1279 | |
---|
1280 | http://zoo-project.org/trac/browser/trunk/zoo-services/ogr/base-vect-ops/cgi-env/Buffer.zcfg |
---|
1281 | |
---|
1282 | You can now ask ZOO Kernel for GetCapabilities, DescribeProcess and Execute for the Buffer Service. |
---|