source: trunk/zoo-services/ogr/base-vect-ops/service.c @ 34

Last change on this file since 34 was 34, checked in by djay, 14 years ago

Make ZOO Kernel able to speak the natural language you teach him by using gettext tools for creating translation files. Add basic french translation .po files as current messages.po. Gettext Domains used are zoo-kernel and zoo-services.

File size: 17.5 KB
Line 
1/**
2 * Author : Gérald FENOY
3 *
4 * Copyright 2008-2009 GeoLabs SARL. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include <libintl.h>
26#include <locale.h>
27#define _(String) dgettext ("zoo-services",String)
28
29#include "cpl_conv.h"
30#include "ogr_api.h"
31#include "ogr_geometry.h"
32#include "geos_c.h"
33#include "service.h"
34
35
36extern "C" {
37#include <libxml/tree.h>
38#include <libxml/parser.h>
39#include <libxml/xpath.h>
40#include <libxml/xpathInternals.h>
41
42#include <openssl/sha.h>
43#include <openssl/hmac.h>
44#include <openssl/evp.h>
45#include <openssl/bio.h>
46#include <openssl/buffer.h>
47
48  void printExceptionReportResponse(maps*,map*);
49  char *base64(const unsigned char *input, int length);
50
51  OGRGeometryH createGeometryFromGML(maps* conf,char* inputStr){
52    xmlInitParser();
53    xmlDocPtr doc = xmlParseMemory(inputStr,strlen(inputStr));
54    xmlChar *xmlbuff;
55    int buffersize;
56    xmlXPathContextPtr xpathCtx;
57    xmlXPathObjectPtr xpathObj;
58    char * xpathExpr="/*/*/*/*/*[local-name()='Polygon' or local-name()='MultiPolygon']";
59    xpathCtx = xmlXPathNewContext(doc);
60    xpathObj = xmlXPathEvalExpression(BAD_CAST xpathExpr,xpathCtx);
61    if(!xpathObj->nodesetval){
62      map* tmp=createMap("text","Unable to parse Input Polygon");
63      addToMap(tmp,"code","InvalidParameterValue");
64      printExceptionReportResponse(conf,tmp);
65      exit(0);
66    }
67    int size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
68    /**
69     * Create a temporary XML document
70     */
71    xmlDocPtr ndoc = xmlNewDoc(BAD_CAST "1.0");
72    /**
73     * Only one polygon should be provided so we use it as the root node.
74     */
75    for(int k=size-1;k>=0;k--){ 
76      xmlDocSetRootElement(ndoc, xpathObj->nodesetval->nodeTab[k]);
77    }
78    xmlDocDumpFormatMemory(ndoc, &xmlbuff, &buffersize, 1);
79    char *tmp=(char*)calloc((xmlStrlen(xmlStrstr(xmlbuff,BAD_CAST "?>"))-1),sizeof(char));
80    sprintf(tmp,"%s",xmlStrstr(xmlbuff,BAD_CAST "?>")+2);
81    xmlXPathFreeObject(xpathObj);
82    xmlXPathFreeContext(xpathCtx);
83    xmlFree(xmlbuff);
84    xmlFreeDoc(doc);
85    xmlFreeDoc(ndoc);
86    xmlCleanupParser();
87#ifdef DEBUG
88    fprintf(stderr,"\nService internal print\n Loading the geometry from GML string ...");
89#endif
90    OGRGeometryH res=OGR_G_CreateFromGML(tmp);
91    free(tmp);
92    if(res==NULL){
93      setMapInMaps(conf,"lenv","message","Unable to call OGR_G_CreatFromGML");
94      return NULL;
95    }
96    else
97      return res;
98  }
99
100  int Simplify(maps*& conf,maps*& inputs,maps*& outputs){
101    maps* cursor=inputs;
102    OGRGeometryH geometry,res;
103    double tolerance;
104    map* tmp0=getMapFromMaps(cursor,"Tolerance","value");
105    if(tmp0==NULL){
106      tolerance=atof("2.0");
107    }
108    else
109      tolerance=atof(tmp0->value);
110    fprintf(stderr,"Tolerance for Simplify %f",tolerance);
111    map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
112    if(!tmp){
113      setMapInMaps(conf,"lenv","message","Unagle to parse the input geometry from InputPolygon");
114      return SERVICE_FAILED;
115    }
116    map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
117    if(tmp1!=NULL){
118      if(strncmp(tmp1->value,"text/js",7)==0 ||
119         strncmp(tmp1->value,"application/json",16)==0)
120        geometry=OGR_G_CreateGeometryFromJson(tmp->value);
121      else
122        geometry=createGeometryFromGML(conf,tmp->value);
123    }
124    else{
125      setMapInMaps(conf,"lenv","message","Unable to find any geometry for InputPolygon");
126      return SERVICE_FAILED;
127    }
128    if(geometry==NULL){
129      setMapInMaps(conf,"lenv","message","Unagle to parse the input geometry from InputPolygon");
130      return SERVICE_FAILED;
131    }
132    fprintf(stderr,"Create GEOSGeometry object");
133    GEOSGeometry* ggeometry=((OGRGeometry *) geometry)->exportToGEOS();
134    GEOSGeometry* gres=GEOSTopologyPreserveSimplify(ggeometry,tolerance);
135    res=OGRGeometryFactory::createFromGEOS(gres);
136    tmp1=getMapFromMaps(outputs,"Result","mimeType");
137    if(tmp1!=NULL){
138      if(strncmp(tmp1->value,"text/js",7)==0 ||
139         strncmp(tmp1->value,"application/json",16)==0){
140        char *tmpS=OGR_G_ExportToJson(res);
141        setMapInMaps(outputs,"Result","value",tmpS);
142        setMapInMaps(outputs,"Result","mimeType","text/plain");
143        setMapInMaps(outputs,"Result","encoding","UTF-8");
144        free(tmpS);
145      }
146      else{
147        char *tmpS=OGR_G_ExportToGML(res);
148        setMapInMaps(outputs,"Result","value",tmpS);
149        setMapInMaps(outputs,"Result","mimeType","text/xml");
150        setMapInMaps(outputs,"Result","encoding","UTF-8");
151        setMapInMaps(outputs,"Result","schema","http://fooa/gml/3.1.0/polygon.xsd");
152        free(tmpS);
153      }
154    }else{
155      char *tmpS=OGR_G_ExportToJson(tmp->value);
156      setMapInMaps(outputs,"Result","value",tmpS);
157      setMapInMaps(outputs,"Result","mimeType","text/plain");
158      setMapInMaps(outputs,"Result","encoding","UTF-8");
159      free(tmpS);
160    }
161    outputs->next=NULL;
162    //GEOSFree(ggeometry);
163    //GEOSFree(gres);
164    OGR_G_DestroyGeometry(res);
165    OGR_G_DestroyGeometry(geometry);
166    return SERVICE_SUCCEEDED;
167  }
168
169
170  int applyOne(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc)(OGRGeometryH)){
171#ifdef DEBUG
172    fprintf(stderr,"\nService internal print\n");
173#endif
174    maps* cursor=inputs;
175    OGRGeometryH geometry,res;
176#ifdef DEBUG
177    dumpMaps(cursor);
178#endif
179    map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
180    if(!tmp)
181      return SERVICE_FAILED;
182    fprintf(stderr,"Service internal print \n");
183    dumpMaps(inputs);
184    fprintf(stderr,"/Service internal print \n");
185    map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
186    fprintf(stderr,"Service internal print \n");
187    dumpMap(tmp1);
188    fprintf(stderr,"/Service internal print \n");
189    if(tmp1!=NULL){
190      if(strncmp(tmp1->value,"text/js",7)==0 ||
191         strncmp(tmp1->value,"application/json",7)==0)
192        geometry=OGR_G_CreateGeometryFromJson(tmp->value);
193      else
194        geometry=createGeometryFromGML(conf,tmp->value);
195    }
196    else
197      geometry=createGeometryFromGML(conf,tmp->value);
198    if(geometry==NULL)
199      return SERVICE_FAILED;
200    res=(*myFunc)(geometry);
201    fprintf(stderr,"Service internal print \n");
202    dumpMaps(outputs);
203    fprintf(stderr,"/Service internal print \n");
204    map *tmp_2=getMapFromMaps(outputs,"Result","mimeType");
205    fprintf(stderr,"Service internal print \n");
206    dumpMap(tmp_2);
207    fprintf(stderr,"/Service internal print \n");
208    if(tmp_2!=NULL){
209      if(strncmp(tmp_2->value,"text/js",7)==0 ||
210         strncmp(tmp_2->value,"application/json",16)==0){
211        char *tmpS=OGR_G_ExportToJson(res);
212        setMapInMaps(outputs,"Result","value",tmpS);
213        setMapInMaps(outputs,"Result","mimeType","text/plain");
214        setMapInMaps(outputs,"Result","encoding","UTF-8");
215        free(tmpS);
216      }
217      else{
218        char *tmpS=OGR_G_ExportToGML(res);
219        setMapInMaps(outputs,"Result","value",tmpS);
220        setMapInMaps(outputs,"Result","mimeType","text/plain");
221        setMapInMaps(outputs,"Result","encoding","UTF-8");
222        free(tmpS);
223
224      }
225    }else{
226      char *tmpS=OGR_G_ExportToJson(res);
227      setMapInMaps(outputs,"Result","value",tmpS);
228      setMapInMaps(outputs,"Result","mimeType","text/plain");
229      setMapInMaps(outputs,"Result","encoding","UTF-8");
230      free(tmpS);
231    }
232    outputs->next=NULL;
233#ifdef DEBUG
234    dumpMaps(outputs);
235    fprintf(stderr,"\nService internal print\n===\n");
236#endif
237    OGR_G_DestroyGeometry(res);
238    OGR_G_DestroyGeometry(geometry);
239    //CPLFree(res);
240    //CPLFree(geometry);
241    fprintf(stderr,"Service internal print \n");
242    dumpMaps(outputs);
243    fprintf(stderr,"/Service internal print \n");
244    return SERVICE_SUCCEEDED;
245  }
246
247#ifdef WIN32
248  __declspec(dllexport)
249#endif
250int Buffer(maps*& conf,maps*& inputs,maps*& outputs){
251   OGRGeometryH geometry,res;
252   map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
253   if(tmp==NULL){
254     setMapInMaps(conf,"lenv","message","Unable to fetch input geometry");
255     return SERVICE_FAILED;
256   }else
257     if(strlen(tmp->value)<=0){
258       setMapInMaps(conf,"lenv","message","Unable to fetch input geometry");
259       return SERVICE_FAILED;
260     }
261   map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
262   if(strncmp(tmp1->value,"application/json",16)==0)
263     geometry=OGR_G_CreateGeometryFromJson(tmp->value);
264   else
265     geometry=createGeometryFromGML(conf,tmp->value);
266   if(geometry==NULL){
267     setMapInMaps(conf,"lenv","message","Unable to parse input geometry");
268     return SERVICE_FAILED;
269   }
270   double bufferDistance;
271   tmp=getMapFromMaps(inputs,"BufferDistance","value");
272   if(tmp==NULL){
273     bufferDistance=atof("10.0");
274   }
275   else
276     bufferDistance=atof(tmp->value);
277   res=OGR_G_Buffer(geometry,bufferDistance,30);
278   tmp1=getMapFromMaps(outputs,"Result","mimeType");
279   if(strncmp(tmp1->value,"application/json",16)==0){
280     char *tmpS=OGR_G_ExportToJson(res);
281     setMapInMaps(outputs,"Result","value",tmpS);
282     setMapInMaps(outputs,"Result","mimeType","text/plain");
283     setMapInMaps(outputs,"Result","encoding","UTF-8");
284     free(tmpS);
285   }
286   else{
287     char *tmpS=OGR_G_ExportToGML(res);
288     setMapInMaps(outputs,"Result","value",tmpS);
289     setMapInMaps(outputs,"Result","mimeType","text/xml");
290     setMapInMaps(outputs,"Result","encoding","UTF-8");
291     setMapInMaps(outputs,"Result","schema","http://fooa/gml/3.1.0/polygon.xsd");
292   }
293   outputs->next=NULL;
294   OGR_G_DestroyGeometry(geometry);
295   OGR_G_DestroyGeometry(res);
296   return SERVICE_SUCCEEDED;
297}
298
299#ifdef WIN32
300  __declspec(dllexport)
301#endif
302  int Boundary(maps*& conf,maps*& inputs,maps*& outputs){
303    return applyOne(conf,inputs,outputs,&OGR_G_GetBoundary);
304  }
305
306#ifdef WIN32
307  __declspec(dllexport)
308#endif
309  int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){
310    return applyOne(conf,inputs,outputs,&OGR_G_ConvexHull);
311  }
312
313
314  OGRGeometryH MY_OGR_G_Centroid(OGRGeometryH hTarget){
315    OGRGeometryH res;
316    res=OGR_G_CreateGeometryFromJson("{\"type\": \"Point\", \"coordinates\": [0,0] }");
317    OGRwkbGeometryType gtype=OGR_G_GetGeometryType(hTarget);
318    if(gtype!=wkbPolygon){
319      hTarget=OGR_G_ConvexHull(hTarget);
320    }
321    int c=OGR_G_Centroid(hTarget,res);
322    return res;
323  }
324
325#ifdef WIN32
326  __declspec(dllexport)
327#endif
328  int Centroid(maps*& conf,maps*& inputs,maps*& outputs){
329    return applyOne(conf,inputs,outputs,&MY_OGR_G_Centroid);
330  }
331
332  int applyTwo(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc)(OGRGeometryH,OGRGeometryH)){
333#ifdef DEBUG
334    fprintf(stderr,"\nService internal print1\n");
335    fflush(stderr);
336#endif
337    fprintf(stderr,"\nService internal print1\n");
338    dumpMaps(inputs);
339    fprintf(stderr,"\nService internal print1\n");
340
341    maps* cursor=inputs;
342    OGRGeometryH geometry1,geometry2;
343    OGRGeometryH res;
344    {
345      map* tmp=getMapFromMaps(inputs,"InputEntity1","value");
346      map* tmp1=getMapFromMaps(inputs,"InputEntity1","mimeType");
347      if(tmp1!=NULL){
348        if(strncmp(tmp1->value,"application/json",16)==0)
349          geometry1=OGR_G_CreateGeometryFromJson(tmp->value);
350        else
351          geometry1=createGeometryFromGML(conf,tmp->value);
352      }
353      else
354        geometry1=createGeometryFromGML(conf,tmp->value);
355    }
356    if(geometry1==NULL){
357      setMapInMaps(conf,"lenv","message","Unable to parse input geometry for InputEntity1.");
358      fprintf(stderr,"SERVICE FAILED !\n");
359      return SERVICE_FAILED;
360    }
361    fprintf(stderr,"\nService internal print1 InputEntity1\n");
362    {
363      map* tmp=getMapFromMaps(inputs,"InputEntity2","value");
364      map* tmp1=getMapFromMaps(inputs,"InputEntity2","mimeType");
365      //#ifdef DEBUG
366      fprintf(stderr,"MY MAP \n[%s] - %i\n",tmp1->value,strncmp(tmp1->value,"application/json",16));
367      //dumpMap(tmp);
368      fprintf(stderr,"MY MAP\n");
369      ///#endif
370      fprintf(stderr,"\nService internal print1 InputEntity2\n");
371      if(tmp1!=NULL){
372        if(strncmp(tmp1->value,"application/json",16)==0){
373          fprintf(stderr,"\nService internal print1 InputEntity2 as JSON\n");
374          geometry2=OGR_G_CreateGeometryFromJson(tmp->value);
375        }
376        else{
377          fprintf(stderr,"\nService internal print1 InputEntity2 as GML\n");
378          geometry2=createGeometryFromGML(conf,tmp->value);
379        }
380      }
381      else
382        geometry2=createGeometryFromGML(conf,tmp->value);
383      fprintf(stderr,"\nService internal print1 InputEntity2 PreFinal\n");
384    }
385    fprintf(stderr,"\nService internal print1 InputEntity2 Final\n");
386    if(geometry2==NULL){
387      setMapInMaps(conf,"lenv","message","Unable to parse input geometry for InputEntity2.");
388      fprintf(stderr,"SERVICE FAILED !\n");
389      return SERVICE_FAILED;
390    }
391    fprintf(stderr,"\nService internal print1\n");
392    res=(*myFunc)(geometry1,geometry2);
393    fprintf(stderr,"\nService internal print1\n");
394    char *tmpS=OGR_G_ExportToJson(res);
395    setMapInMaps(outputs,"Result","value",tmpS);
396    setMapInMaps(outputs,"Result","mimeType","text/plain");
397    setMapInMaps(outputs,"Result","encoding","UTF-8");
398    free(tmpS);
399    OGR_G_DestroyGeometry(geometry1);
400    OGR_G_DestroyGeometry(geometry2);
401    OGR_G_DestroyGeometry(res);
402    return SERVICE_SUCCEEDED;
403  }
404 
405#ifdef WIN32
406  __declspec(dllexport)
407#endif
408  int Difference(maps*& conf,maps*& inputs,maps*& outputs){
409    return applyTwo(conf,inputs,outputs,&OGR_G_Difference);
410  }
411
412#ifdef WIN32
413  __declspec(dllexport)
414#endif
415  int SymDifference(maps*& conf,maps*& inputs,maps*& outputs){
416    return applyTwo(conf,inputs,outputs,&OGR_G_SymmetricDifference);
417  }
418
419#ifdef WIN32
420  __declspec(dllexport)
421#endif
422  int Intersection(maps*& conf,maps*& inputs,maps*& outputs){
423    return applyTwo(conf,inputs,outputs,&OGR_G_Intersection);
424  }
425
426#ifdef WIN32
427  __declspec(dllexport)
428#endif
429  int Union(maps*& conf,maps*& inputs,maps*& outputs){
430    return applyTwo(conf,inputs,outputs,&OGR_G_Union);
431  }
432
433#ifdef WIN32
434  __declspec(dllexport)
435#endif
436  int Distance(maps*& conf,maps*& inputs,maps*& outputs){
437#ifdef DEBUG
438    fprintf(stderr,"\nService internal print1\n");
439#endif
440    fflush(stderr);
441    maps* cursor=inputs;
442    OGRGeometryH geometry1,geometry2;
443    double res;
444    {
445      map* tmp=getMapFromMaps(inputs,"InputEntity1","value");
446      map* tmp1=getMapFromMaps(inputs,"InputEntity1","mimeType");
447#ifdef DEBUG
448      fprintf(stderr,"MY MAP\n");
449      dumpMap(tmp1);
450      dumpMaps(inputs);
451      fprintf(stderr,"MY MAP\n");
452#endif
453      if(tmp1!=NULL){
454        if(strncmp(tmp1->value,"application/json",16)==0)
455          geometry1=OGR_G_CreateGeometryFromJson(tmp->value);
456        else
457          geometry1=createGeometryFromGML(conf,tmp->value);
458      }
459      else
460        geometry1=createGeometryFromGML(conf,tmp->value);
461    }
462    if(geometry1==NULL){
463      setMapInMaps(conf,"lenv","message","Unable to parse input geometry for InputEntity1.");
464      fprintf(stderr,"SERVICE FAILED !\n");
465      return SERVICE_FAILED;
466    }
467    {
468      map* tmp=getMapFromMaps(inputs,"InputEntity2","value");
469      map* tmp1=getMapFromMaps(inputs,"InputEntity2","mimeType");
470#ifdef DEBUG
471      fprintf(stderr,"MY MAP\n");
472      dumpMap(tmp1);
473      dumpMaps(inputs);
474      fprintf(stderr,"MY MAP\n");
475#endif
476      if(tmp1!=NULL){
477        if(strncmp(tmp1->value,"application/json",16)==0)
478          geometry2=OGR_G_CreateGeometryFromJson(tmp->value);
479        else
480          geometry2=createGeometryFromGML(conf,tmp->value);
481      }
482      else
483        geometry2=createGeometryFromGML(conf,tmp->value);
484    }
485    if(geometry2==NULL){
486      setMapInMaps(conf,"lenv","message",_("Unable to parse input geometry for InputEntity2."));
487      fprintf(stderr,"SERVICE FAILED !\n");
488      return SERVICE_FAILED;
489    }
490    res=OGR_G_Distance(geometry1,geometry2);   
491    char tmpres[100];
492    sprintf(tmpres,"%d",res);
493    setMapInMaps(outputs,"Distance","value",tmpres);
494    setMapInMaps(outputs,"Distance","dataType","float");
495#ifdef DEBUG
496    dumpMaps(outputs);
497    fprintf(stderr,"\nService internal print\n===\n");
498#endif
499    return SERVICE_SUCCEEDED;
500  }
501
502#ifdef WIN32
503  __declspec(dllexport)
504#endif
505  int GetArea(maps*& conf,maps*& inputs,maps*& outputs){
506    fprintf(stderr,"GETAREA \n");
507    double res;
508    /**
509     * Extract Geometry from the InputPolygon value
510     */
511    OGRGeometryH geometry;
512    map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
513    if(tmp==NULL){
514      setMapInMaps(conf,"lenv","message","Unable to parse input geometry from InputPolygon");
515      return SERVICE_FAILED;
516    }
517    fprintf(stderr,"geometry creation %s \n",tmp->value);
518    geometry=createGeometryFromGML(conf,tmp->value);
519    if(geometry==NULL){
520      setMapInMaps(conf,"lenv","message","Unable to parse input geometry from InputPolygon");
521      return SERVICE_FAILED;
522    }
523    fprintf(stderr,"geometry created %s \n",tmp->value);
524    res=OGR_G_GetArea(geometry);
525    fprintf(stderr,"area %d \n",res);
526    /**
527     * Filling the outputs
528     */
529    char tmp1[100];
530    sprintf(tmp1,"%d",res);
531    setMapInMaps(outputs,"Area","value",tmp1);
532    setMapInMaps(outputs,"Area","dataType","float");
533#ifdef DEBUG
534    dumpMaps(outputs);
535#endif
536    return SERVICE_SUCCEEDED;
537  }
538
539}
Note: See TracBrowser for help on using the repository browser.

Search

ZOO Sponsors

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

Become a sponsor !

Knowledge partners

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

Become a knowledge partner

Related links

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