source: trunk/zoo-project/zoo-kernel/service_internal_ms.c @ 584

Last change on this file since 584 was 579, checked in by djay, 10 years ago

Add initial doxygen comments in some C files, for future documentation generation.

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc
File size: 34.4 KB
Line 
1/*
2 * Author : Gérald FENOY
3 *
4 *  Copyright 2010-2011 Fondazione Edmund Mach. 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#ifdef USE_MS
26#ifndef WIN32
27#define CLASS class
28#else
29#define CLASS _class
30#endif
31#include "service_internal_ms.h"
32
33/**
34 * Map composed by a main.cfg maps name as key and the corresponding
35 * MapServer Mafile Metadata name to use
36 * see doc from here :
37 *  - http://mapserver.org/ogc/wms_server.html
38 *  - http://mapserver.org/ogc/wfs_server.html
39 *  - http://mapserver.org/ogc/wcs_server.html
40 */
41map* getCorrespondance(){
42  map* res=createMap("encoding","ows_encoding");
43  addToMap(res,"abstract","ows_abstract");
44  addToMap(res,"title","ows_title");
45  addToMap(res,"keywords","ows_keywordlist");
46  addToMap(res,"fees","ows_fees");
47  addToMap(res,"accessConstraints","ows_accessconstraints");
48  addToMap(res,"providerName","ows_attribution_title");
49  addToMap(res,"providerSite","ows_service_onlineresource");
50  addToMap(res,"individualName","ows_contactperson");
51  addToMap(res,"positionName","ows_contactposition");
52  addToMap(res,"providerName","ows_contactorganization");
53  addToMap(res,"role","ows_role");
54  addToMap(res,"addressType","ows_addresstype");
55  addToMap(res,"addressCity","ows_city");
56  addToMap(res,"addressDeliveryPoint","ows_address");
57  addToMap(res,"addressPostalCode","ows_postcode");
58  addToMap(res,"addressAdministrativeArea","ows_stateorprovince");
59  addToMap(res,"addressCountry","ows_country");
60  addToMap(res,"phoneVoice","ows_contactvoicetelephone");
61  addToMap(res,"phoneFacsimile","ows_contactfacsimiletelephone");
62  addToMap(res,"addressElectronicMailAddress","ows_contactelectronicmailaddress");
63  // Missing Madatory Informations
64  addToMap(res,"hoursOfService","ows_hoursofservice");
65  addToMap(res,"contactInstructions","ows_contactinstructions");
66  return res;
67}
68
69void setMapSize(maps* output,double minx,double miny,double maxx,double maxy){
70  double maxWidth=640;
71  double maxHeight=480;
72  double deltaX=maxx-minx;
73  double deltaY=maxy-miny;
74  double qWidth;
75  qWidth=maxWidth/deltaX;
76  double qHeight;
77  qHeight=maxHeight/deltaY;
78#ifdef DEBUGMS
79  fprintf(stderr,"deltaX : %.15f \ndeltaY : %.15f\n",deltaX,deltaY);
80  fprintf(stderr,"qWidth : %.15f \nqHeight : %.15f\n",qWidth,qHeight);
81#endif
82
83  double width=deltaX*qWidth;
84  double height=height=deltaY*qWidth;
85  if(deltaX<deltaY){
86    width=deltaX*qHeight;
87    height=deltaY*qHeight;
88  }
89  if(height<0)
90    height=-height;
91  if(width<0)
92    width=-width;
93  char sWidth[1024];
94  char sHeight[1024];
95  sprintf(sWidth,"%.3f",width);
96  sprintf(sHeight,"%.3f",height);
97#ifdef DEBUGMS
98  fprintf(stderr,"sWidth : %.15f \nsHeight : %.15f\n",sWidth,sHeight);
99#endif
100  if(output!=NULL){
101    addToMap(output->content,"width",sWidth);
102    addToMap(output->content,"height",sHeight);
103  }
104}
105
106void setReferenceUrl(maps* m,maps* tmpI){
107  outputMapfile(m,tmpI);
108  map *msUrl=getMapFromMaps(m,"main","mapserverAddress");
109  map *msOgcVersion=getMapFromMaps(m,"main","msOgcVersion");
110  map *dataPath=getMapFromMaps(m,"main","dataPath");
111  map *sid=getMapFromMaps(m,"lenv","usid");
112  map* format=getMap(tmpI->content,"mimeType");
113  map* rformat=getMap(tmpI->content,"requestedMimeType");
114  map* width=getMap(tmpI->content,"width");
115  map* height=getMap(tmpI->content,"height");
116  map* protoMap=getMap(tmpI->content,"msOgc");
117  map* versionMap=getMap(tmpI->content,"msOgcVersion");
118  char options[3][5][25]={
119    {"WMS","1.3.0","GetMap","layers=%s","wms_extent"},
120    {"WFS","1.1.0","GetFeature","typename=%s","wcs_extent"},
121    {"WCS","1.1.0","GetCoverage","coverage=%s","wcs_extent"}
122  };
123  int proto=0;
124  if(rformat==NULL){
125    rformat=getMap(tmpI->content,"mimeType");
126  }
127  if(strncasecmp(rformat->value,"text/xml",8)==0)
128    proto=1;
129  if(strncasecmp(rformat->value,"image/tiff",10)==0)
130    proto=2;
131  if(protoMap!=NULL){
132    if(strncasecmp(protoMap->value,"WMS",3)==0)
133      proto=0;
134    else{
135      if(strncasecmp(protoMap->value,"WFS",3)==0)
136        proto=1;
137      else 
138        proto=2;
139    }
140  }
141  char *protoVersion=options[proto][1];
142  if(proto==1){
143    if(msOgcVersion!=NULL)
144      protoVersion=msOgcVersion->value;
145    if(versionMap!=NULL)
146      protoVersion=versionMap->value;
147  }
148
149  map* extent=getMap(tmpI->content,options[proto][4]);
150  map* crs=getMap(tmpI->content,"crs");
151  int hasCRS=1;
152  if(crs==NULL){
153    crs=getMapFromMaps(m,"main","crs");
154    if(crs==NULL){
155      crs=createMap("crs","epsg:4326");
156      hasCRS=0;
157    }
158  }
159  char layers[128];
160  sprintf(layers,options[proto][3],tmpI->name);
161
162  char* webService_url=(char*)malloc((strlen(msUrl->value)+strlen(format->value)+strlen(tmpI->name)+strlen(width->value)+strlen(height->value)+strlen(extent->value)+256)*sizeof(char));
163
164  if(proto>0){
165    sprintf(webService_url,
166            "%s?map=%s/%s_%s.map&request=%s&service=%s&version=%s&%s&format=%s&bbox=%s&crs=%s",
167            msUrl->value,
168            dataPath->value,
169            tmpI->name,
170            sid->value,
171            options[proto][2],
172            options[proto][0],
173            protoVersion,
174            layers,
175            rformat->value,
176            extent->value,
177            crs->value
178            );
179  }
180  else{
181    sprintf(webService_url,
182            "%s?map=%s/%s_%s.map&request=%s&service=%s&version=%s&%s&width=%s&height=%s&format=%s&bbox=%s&crs=%s",
183            msUrl->value,
184            dataPath->value,
185            tmpI->name,
186            sid->value,
187            options[proto][2],
188            options[proto][0],
189            protoVersion,
190            layers,
191            width->value,
192            height->value,
193            rformat->value,
194            extent->value,
195            crs->value
196            );
197  }
198  if(hasCRS==0){
199    freeMap(&crs);
200    free(crs);
201  }
202  addToMap(tmpI->content,"Reference",webService_url);
203  free(webService_url);
204}
205
206/**
207 * Set projection using Authority Code and Name if available or fallback to
208 * proj4 definition if available or fallback to default EPSG:4326
209 */
210void setSrsInformations(maps* output,mapObj* m,layerObj* myLayer,
211                        char* pszProjection){
212  OGRSpatialReferenceH  hSRS;
213  map* msSrs=NULL;
214  hSRS = OSRNewSpatialReference(NULL);
215  if( pszProjection!=NULL && strlen(pszProjection)>1){
216    if(OSRImportFromWkt( hSRS, &pszProjection ) == CE_None ){
217      char *proj4Str=NULL;
218      if(OSRGetAuthorityName(hSRS,NULL)!=NULL && 
219         OSRGetAuthorityCode(hSRS,NULL)!=NULL){
220        char tmpSrs[20];
221        sprintf(tmpSrs,"%s:%s",
222                OSRGetAuthorityName(hSRS,NULL),OSRGetAuthorityCode(hSRS,NULL));
223        msLoadProjectionStringEPSG(&m->projection,tmpSrs);
224        msLoadProjectionStringEPSG(&myLayer->projection,tmpSrs);
225       
226        char tmpSrss[256];
227        sprintf(tmpSrss,"EPSG:4326 EPSG:900913 %s",tmpSrs);
228       
229        msInsertHashTable(&(m->web.metadata), "ows_srs", tmpSrss);
230        msInsertHashTable(&(myLayer->metadata), "ows_srs", tmpSrss);
231       
232#ifdef DEBUGMS
233        fprintf(stderr,"isGeo %b\n\n",OSRIsGeographic(hSRS)==TRUE);
234#endif
235        if(output!=NULL){
236          if(OSRIsGeographic(hSRS)==TRUE)
237            addToMap(output->content,"crs_isGeographic","true");
238          else
239            addToMap(output->content,"crs_isGeographic","false");
240          addToMap(output->content,"crs",tmpSrs);
241        }
242      }
243      else{
244        OSRExportToProj4(hSRS,&proj4Str);
245        if(proj4Str!=NULL && strlen(proj4Str)>0){
246#ifdef DEBUGMS
247          fprintf(stderr,"PROJ (%s)\n",proj4Str);
248#endif
249          msLoadProjectionString(&(m->projection),proj4Str);
250          msLoadProjectionString(&(myLayer->projection),proj4Str);
251          if(output!=NULL){ 
252            if(OSRIsGeographic(hSRS)==TRUE)
253              addToMap(output->content,"crs_isGeographic","true");
254            else
255              addToMap(output->content,"crs_isGeographic","false");
256          }
257          free(proj4Str);
258        }
259        else{
260          msLoadProjectionStringEPSG(&m->projection,"EPSG:4326");
261          msLoadProjectionStringEPSG(&myLayer->projection,"EPSG:4326");
262          if(output!=NULL){
263            addToMap(output->content,"crs_isGeographic","true");
264          }
265        }
266        if(output!=NULL){
267          addToMap(output->content,"crs","EPSG:4326");
268          addToMap(output->content,"real_extent","true");
269        }
270        msInsertHashTable(&(m->web.metadata),"ows_srs", "EPSG:4326 EPSG:900913");
271        msInsertHashTable(&(myLayer->metadata),"ows_srs","EPSG:4326 EPSG:900913");
272      }
273    }
274  }
275  else{
276    if(output!=NULL){
277      msSrs=getMap(output->content,"msSrs");
278    }
279    if(msSrs!=NULL){
280      msLoadProjectionStringEPSG(&m->projection,msSrs->value);
281      msLoadProjectionStringEPSG(&myLayer->projection,msSrs->value);
282      char tmpSrs[128];
283      sprintf(tmpSrs,"%s EPSG:4326 EPSG:900913",msSrs->value);
284      msInsertHashTable(&(m->web.metadata),"ows_srs",tmpSrs);
285      msInsertHashTable(&(myLayer->metadata),"ows_srs",tmpSrs);
286    }else{
287      msLoadProjectionStringEPSG(&m->projection,"EPSG:4326");
288      msLoadProjectionStringEPSG(&myLayer->projection,"EPSG:4326");
289      msInsertHashTable(&(m->web.metadata),"ows_srs","EPSG:4326 EPSG:900913");
290      msInsertHashTable(&(myLayer->metadata),"ows_srs","EPSG:4326 EPSG:900913");
291    }
292    if(output!=NULL){
293      addToMap(output->content,"crs",msSrs->value);
294      addToMap(output->content,"crs_isGeographic","true");
295    }
296  }
297
298  OSRDestroySpatialReference( hSRS );
299}
300
301void setMsExtent(maps* output,mapObj* m,layerObj* myLayer,
302                 double minX,double minY,double maxX,double maxY){
303  msMapSetExtent(m,minX,minY,maxX,maxY);
304#ifdef DEBUGMS
305  fprintf(stderr,"Extent %.15f %.15f %.15f %.15f\n",minX,minY,maxX,maxY);
306#endif
307  char tmpExtent[1024];
308  sprintf(tmpExtent,"%.15f %.15f %.15f %.15f",minX,minY,maxX,maxY);
309#ifdef DEBUGMS
310  fprintf(stderr,"Extent %s\n",tmpExtent);
311#endif
312  msInsertHashTable(&(myLayer->metadata), "ows_extent", tmpExtent);
313 
314  if(output!=NULL){
315    map* test=getMap(output->content,"real_extent");
316    if(test!=NULL){
317      pointObj min, max;
318      projectionObj tempSrs;
319      min.x = m->extent.minx;
320      min.y = m->extent.miny;
321      max.x = m->extent.maxx;
322      max.y = m->extent.maxy;
323      char tmpSrsStr[1024];
324      msInitProjection(&tempSrs);
325      msLoadProjectionStringEPSG(&tempSrs,"EPSG:4326");
326
327      msProjectPoint(&(m->projection),&tempSrs,&min);
328      msProjectPoint(&m->projection,&tempSrs,&max);
329     
330      sprintf(tmpExtent,"%.3f,%.3f,%.3f,%.3f",min.y,min.x,max.y,max.x);
331      map* isGeo=getMap(output->content,"crs_isGeographic");
332#ifdef DEBUGMS
333      fprintf(stderr,"isGeo = %s\n",isGeo->value);
334#endif
335      if(isGeo!=NULL && strcasecmp("true",isGeo->value)==0)
336        sprintf(tmpExtent,"%f,%f,%f,%f", minY,minX, maxY, maxX);
337      addToMap(output->content,"wms_extent",tmpExtent);
338      sprintf(tmpSrsStr,"%.3f,%.3f,%.3f,%.3f",min.x,min.y,max.x,max.y);
339      addToMap(output->content,"wcs_extent",tmpExtent);
340    }else{
341      sprintf(tmpExtent,"%f,%f,%f,%f",minX, minY, maxX, maxY);
342      map* isGeo=getMap(output->content,"crs_isGeographic");
343      if(isGeo!=NULL){
344#ifdef DEBUGMS
345        fprintf(stderr,"isGeo = %s\n",isGeo->value);
346#endif
347        if(isGeo!=NULL && strcasecmp("true",isGeo->value)==0)
348          sprintf(tmpExtent,"%f,%f,%f,%f", minY,minX, maxY, maxX);
349      }
350      addToMap(output->content,"wms_extent",tmpExtent); 
351      sprintf(tmpExtent,"%.3f,%.3f,%.3f,%.3f",minX,minY,maxX,maxY);
352      addToMap(output->content,"wcs_extent",tmpExtent);
353    }
354  }
355
356  setMapSize(output,minX,minY,maxX,maxY);
357}
358
359int tryOgr(maps* conf,maps* output,mapObj* m){
360
361  map* tmpMap=getMap(output->content,"storage");
362  char *pszDataSource=tmpMap->value;
363
364  /**
365   * Try to open the DataSource using OGR
366   */
367  OGRRegisterAll();
368  /**
369   * Try to load the file as ZIP
370   */
371
372  OGRDataSourceH poDS1 = NULL;
373  OGRSFDriverH *poDriver1 = NULL;
374  char *dsName=(char*)malloc((8+strlen(pszDataSource)+1)*sizeof(char));
375  char *odsName=zStrdup(pszDataSource);
376  char *sdsName=zStrdup(pszDataSource);
377  char *demo=strstr(odsName,".");
378  sdsName[strlen(sdsName)-(strlen(demo)-1)]='d';
379  sdsName[strlen(sdsName)-(strlen(demo)-2)]='i';
380  sdsName[strlen(sdsName)-(strlen(demo)-3)]='r';
381  sdsName[strlen(sdsName)-(strlen(demo)-4)]=0;
382
383  odsName[strlen(odsName)-(strlen(demo)-1)]='z';
384  odsName[strlen(odsName)-(strlen(demo)-2)]='i';
385  odsName[strlen(odsName)-(strlen(demo)-3)]='p';
386  odsName[strlen(odsName)-(strlen(demo)-4)]=0;
387  sprintf(dsName,"/vsizip/%s",odsName);
388
389#ifdef DEBUGMS
390  fprintf(stderr,"Try loading %s, %s, %s\n",dsName,odsName,dsName);
391#endif
392
393  FILE* file = fopen(pszDataSource, "rb");
394  FILE* fileZ = fopen(odsName, "wb");
395  free(odsName);
396  fseek(file, 0, SEEK_END);
397  unsigned long fileLen=ftell(file);
398  fseek(file, 0, SEEK_SET);
399  char *buffer=(char *)malloc(fileLen+1);
400  fread(buffer, fileLen, 1, file);
401  fwrite(buffer,fileLen, 1, fileZ);
402  fclose(file);
403  fclose(fileZ);
404  free(buffer);
405#ifdef DEBUGMS
406  fprintf(stderr,"Try loading %s",dsName);
407#endif
408  poDS1 = OGROpen( dsName, FALSE, poDriver1 );
409  if( poDS1 == NULL ){
410    fprintf(stderr,"Unable to access the DataSource as ZIP File\n");
411    setMapInMaps(conf,"lenv","message","Unable to open datasource in read only mode");
412    OGR_DS_Destroy(poDS1);
413  }else{
414#ifdef DEBUGMS
415    fprintf(stderr,"The DataSource is a  ZIP File\n");
416#endif
417    char** demo=VSIReadDir(dsName);
418    int i=0;
419    zMkdir(sdsName
420#ifndef WIN32
421                ,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
422#endif
423                );
424    while(demo[i]!=NULL){
425#ifdef DEBUGMS
426      fprintf(stderr,"ZIP File content : %s\n",demo[i]);
427#endif
428      char *tmpDs=(char*)malloc((strlen(dsName)+strlen(demo[i])+2)*sizeof(char));
429      sprintf(tmpDs,"%s/%s",dsName,demo[i]);
430      fprintf(stderr,"read : %s\n",tmpDs);
431     
432      VSILFILE* vsif=VSIFOpenL(tmpDs,"rb");
433#ifdef DEBUGMS
434      fprintf(stderr,"open : %s\n",tmpDs);
435#endif
436      VSIFSeekL(vsif,0,SEEK_END);
437      vsi_l_offset size=VSIFTellL(vsif);
438#ifdef DEBUGMS
439      fprintf(stderr,"size : %d\n",size);
440#endif
441      VSIFSeekL(vsif,0,SEEK_SET);
442      char *vsifcontent=(char*) malloc(((int)size+1)*sizeof(char));
443      VSIFReadL(vsifcontent,1,(size_t)size,vsif);
444      char *fpath=(char*) malloc((strlen(sdsName)+strlen(demo[1])+2)*sizeof(char));
445      sprintf(fpath,"%s/%s",sdsName,demo[i]);
446      int f=zOpen(fpath,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
447      zWrite(f,vsifcontent,(int)size);
448      close(f);
449      chmod(fpath,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
450      char* tmpP=strstr(fpath,".shp");
451      if(tmpP==NULL)
452        tmpP=strstr(fpath,".SHP");
453      if(tmpP!=NULL){
454#ifdef DEBUGMS
455        fprintf(stderr,"*** DEBUG %s\n",strstr(tmpP,"."));
456#endif
457        if( strcmp(tmpP,".shp")==0 || strcmp(tmpP,".SHP")==0 ){
458          tmpMap=getMap(output->content,"storage");
459          free(tmpMap->value);
460          tmpMap->value=(char*) malloc((strlen(fpath)+1)*sizeof(char));
461          sprintf(tmpMap->value,"%s",fpath);
462          pszDataSource=tmpMap->value;
463#ifdef DEBUGMS
464          fprintf(stderr,"*** DEBUG %s\n",pszDataSource);
465#endif
466        }
467      }
468      VSIFCloseL(vsif);
469      i++;
470    }
471  }
472  free(sdsName);
473  free(dsName);
474
475  OGRDataSourceH poDS = NULL;
476  OGRSFDriverH *poDriver = NULL;
477  poDS = OGROpen( pszDataSource, FALSE, poDriver );
478  if( poDS == NULL ){
479#ifdef DEBUGMS
480    fprintf(stderr,"Unable to access the DataSource %s\n",pszDataSource);
481#endif
482    setMapInMaps(conf,"lenv","message","Unable to open datasource in read only mode");
483    OGR_DS_Destroy(poDS);
484    OGRCleanupAll();
485#ifdef DEBUGMS
486    fprintf(stderr,"Unable to access the DataSource, exit! \n"); 
487#endif
488    return -1;
489  }
490
491  int iLayer = 0;
492  for( iLayer=0; iLayer < OGR_DS_GetLayerCount(poDS); iLayer++ ){
493    OGRLayerH poLayer = OGR_DS_GetLayer(poDS,iLayer);
494
495    if( poLayer == NULL ){
496#ifdef DEBUGMS
497      fprintf(stderr,"Unable to access the DataSource Layer \n");
498#endif
499      setMapInMaps(conf,"lenv","message","Unable to open datasource in read only mode");
500      return -1;
501    }
502
503    /**
504     * Add a new layer set name, data
505     */
506    if(msGrowMapLayers(m)==NULL){
507      return -1;
508    }
509    if(initLayer((m->layers[m->numlayers]), m) == -1){
510      return -1;
511    }
512
513    layerObj* myLayer=m->layers[m->numlayers];
514#ifdef DEBUGMS
515    dumpMaps(output);
516#endif
517    myLayer->name = zStrdup(output->name);
518    myLayer->tileitem=NULL;
519    myLayer->data = zStrdup(OGR_L_GetName(poLayer));
520    myLayer->connection = zStrdup(pszDataSource);
521    myLayer->index = m->numlayers;
522    myLayer->dump = MS_TRUE;
523    myLayer->status = MS_ON;
524    msConnectLayer(myLayer,MS_OGR,pszDataSource);
525
526    /**
527     * Detect the Geometry Type or use Polygon
528     */
529    if(OGR_L_GetGeomType(poLayer) != wkbUnknown){
530      switch(OGR_L_GetGeomType(poLayer)){
531      case wkbPoint:
532      case wkbMultiPoint:
533      case wkbPoint25D:
534      case wkbMultiPoint25D:
535#ifdef DEBUGMS
536        fprintf(stderr,"POINT DataSource Layer \n");
537#endif
538        myLayer->type = MS_LAYER_POINT;
539        break;
540      case wkbLineString :
541      case wkbMultiLineString :
542      case wkbLineString25D:
543      case wkbMultiLineString25D:
544#ifdef DEBUGMS
545        fprintf(stderr,"LINE DataSource Layer \n");
546#endif
547        myLayer->type = MS_LAYER_LINE;
548        break;
549      case wkbPolygon:
550      case wkbMultiPolygon:
551      case wkbPolygon25D:
552      case wkbMultiPolygon25D:
553#ifdef DEBUGMS
554        fprintf(stderr,"POLYGON DataSource Layer \n");
555#endif
556        myLayer->type = MS_LAYER_POLYGON;
557        break;
558      default:
559        myLayer->type = MS_LAYER_POLYGON;
560        break;
561      }
562    }else
563      myLayer->type = MS_LAYER_POLYGON;
564
565    /**
566     * Detect spatial reference or use WGS84
567     */
568    OGRSpatialReferenceH srs=OGR_L_GetSpatialRef(poLayer);
569    if(srs!=NULL){
570      char *wkt=NULL;
571      OSRExportToWkt(srs,&wkt);
572      setSrsInformations(output,m,myLayer,wkt);
573      free(wkt);
574    }
575    else{
576      addToMap(output->content,"crs","EPSG:4326");
577      addToMap(output->content,"crs_isGeographic","true");
578      msLoadProjectionStringEPSG(&m->projection,"EPSG:4326");
579      msInsertHashTable(&(m->web.metadata), "ows_srs", "EPSG:4326 EPSG:900913");
580      msInsertHashTable(&(myLayer->metadata), "ows_srs", "EPSG:4326 EPSG:900913");
581    }
582
583    OGREnvelope oExt;
584    if (OGR_L_GetExtent(poLayer,&oExt, TRUE) == OGRERR_NONE){
585      setMsExtent(output,m,myLayer,oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY);
586    }
587 
588    /**
589     * Detect the FID column or use the first attribute field as FID
590     */
591    char *fid=(char*)OGR_L_GetFIDColumn(poLayer);
592    if(strlen(fid)==0){
593      OGRFeatureDefnH def=OGR_L_GetLayerDefn(poLayer);
594      int fIndex=0;
595      for(fIndex=0;fIndex<OGR_FD_GetFieldCount(def);fIndex++){
596        OGRFieldDefnH fdef=OGR_FD_GetFieldDefn(def,fIndex);
597        fid=(char*)OGR_Fld_GetNameRef(fdef);
598        break;
599      }
600    }
601    msInsertHashTable(&(myLayer->metadata), "gml_featureid", fid);
602    msInsertHashTable(&(myLayer->metadata), "gml_include_items", "all");
603    msInsertHashTable(&(myLayer->metadata), "ows_name", output->name);
604    map* tmpMap=getMap(output->content,"title");
605    if(tmpMap!=NULL)
606      msInsertHashTable(&(myLayer->metadata), "ows_title", tmpMap->value);
607    else
608      msInsertHashTable(&(myLayer->metadata), "ows_title", "Default Title");
609   
610    if(msGrowLayerClasses(myLayer) == NULL)
611      return -1;
612    if(initClass((myLayer->CLASS[myLayer->numclasses])) == -1)
613      return -1;
614    myLayer->CLASS[myLayer->numclasses]->type = myLayer->type;
615    if(msGrowClassStyles(myLayer->CLASS[myLayer->numclasses]) == NULL)
616      return -1;
617    if(initStyle(myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]) == -1)
618      return -1;
619    /**
620     * Apply msStyle else fallback to the default style
621     */
622    tmpMap=getMap(output->content,"msStyle");
623    if(tmpMap!=NULL)
624      msUpdateStyleFromString(myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles],tmpMap->value,0);
625    else{
626      /**
627       * Set style
628       */
629      myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->color.red=125;
630      myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->color.green=125;
631      myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->color.blue=255;
632      myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->outlinecolor.red=80;
633      myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->outlinecolor.green=80;
634      myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->outlinecolor.blue=80;
635     
636      /**
637       * Set specific style depending on type
638       */
639      if(myLayer->type == MS_LAYER_POLYGON)
640        myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->width=3;
641      if(myLayer->type == MS_LAYER_LINE){
642        myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->width=3;
643        myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->outlinewidth=1.5;
644      }
645      if(myLayer->type == MS_LAYER_POINT){
646        myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->symbol=1;
647        myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->size=15;
648      }
649     
650    }
651    myLayer->CLASS[myLayer->numclasses]->numstyles++;
652    myLayer->numclasses++;
653   
654    m->layerorder[m->numlayers] = m->numlayers;
655    m->numlayers++;
656
657  }
658
659  OGR_DS_Destroy(poDS);
660  OGRCleanupAll();
661
662  return 1;
663}
664
665
666int tryGdal(maps* conf,maps* output,mapObj* m){
667  map* tmpMap=getMap(output->content,"storage");
668  char *pszFilename=tmpMap->value;
669  GDALDatasetH hDataset;
670  GDALRasterBandH hBand;
671  double adfGeoTransform[6];
672  int i, iBand;
673 
674  /**
675   * Try to open the DataSource using GDAL
676   */
677  GDALAllRegister();
678  hDataset = GDALOpen( pszFilename, GA_ReadOnly );
679  if( hDataset == NULL ){
680#ifdef DEBUGMS
681    fprintf(stderr,"Unable to access the DataSource %s \n",pszFilename);
682#endif
683    setMapInMaps(conf,"lenv","message","gdalinfo failed - unable to open");
684    GDALDestroyDriverManager();
685    return -1;
686  }
687#ifdef DEBUGMS
688  fprintf(stderr,"Accessing the DataSource %s %d\n",pszFilename,__LINE__);
689#endif
690
691  /**
692   * Add a new layer set name, data
693   */
694  if(msGrowMapLayers(m)==NULL){
695    return -1;
696  }
697  if(initLayer((m->layers[m->numlayers]), m) == -1){
698    return -1;
699  }
700  m->layers[m->numlayers]->index=m->numlayers;
701
702  layerObj* myLayer=m->layers[m->numlayers];
703  myLayer->name = zStrdup(output->name);
704  myLayer->tileitem=NULL;
705  myLayer->data = zStrdup(pszFilename);
706  myLayer->index = m->numlayers;
707  myLayer->dump = MS_TRUE;
708  myLayer->status = MS_ON;
709  myLayer->type = MS_LAYER_RASTER;
710
711  char *title=output->name;
712  tmpMap=getMap(output->content,"title");
713  if(tmpMap!=NULL)
714    title=tmpMap->value;
715  char *abstract=output->name;
716  tmpMap=getMap(output->content,"abstract");
717  if(tmpMap!=NULL)
718    abstract=tmpMap->value;
719
720  msInsertHashTable(&(myLayer->metadata), "ows_label", title);
721  msInsertHashTable(&(myLayer->metadata), "ows_title", title);
722  msInsertHashTable(&(myLayer->metadata), "ows_abstract", abstract);
723  msInsertHashTable(&(myLayer->metadata), "ows_rangeset_name", output->name);
724  msInsertHashTable(&(myLayer->metadata), "ows_rangeset_label", title);
725
726  /**
727   * Set Map Size to the raster size
728   */
729  m->width=GDALGetRasterXSize( hDataset );
730  m->height=GDALGetRasterYSize( hDataset );
731 
732  /**
733   * Set projection using Authority Code and Name if available or fallback to
734   * proj4 definition if available or fallback to default EPSG:4326
735   */
736  const char *tRef=GDALGetProjectionRef( hDataset );
737  if( tRef != NULL && strlen(tRef)>0 ){
738    char *pszProjection;
739    pszProjection = (char *) GDALGetProjectionRef( hDataset );
740    //#ifdef DEBUGMS
741    fprintf(stderr,"Accessing the DataSource %s\n",GDALGetProjectionRef( hDataset ));
742    //#endif
743    setSrsInformations(output,m,myLayer,pszProjection);
744  }else{
745    fprintf(stderr,"NO SRS FOUND ! %s\n",GDALGetProjectionRef( hDataset ));   
746  }
747
748
749  /**
750   * Set extent
751   */
752  if( GDALGetGeoTransform( hDataset, adfGeoTransform ) == CE_None ){
753    if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 ){
754
755      double minX = adfGeoTransform[0]
756        + adfGeoTransform[2] * GDALGetRasterYSize(hDataset);
757      double minY = adfGeoTransform[3]
758        + adfGeoTransform[5] * GDALGetRasterYSize(hDataset);
759
760      double maxX = adfGeoTransform[0]
761        + adfGeoTransform[1] * GDALGetRasterXSize(hDataset);
762      double maxY = adfGeoTransform[3]
763        + adfGeoTransform[4] * GDALGetRasterXSize(hDataset);
764
765       setMsExtent(output,m,myLayer,minX,minY,maxX,maxY);
766
767    }
768  }
769
770  /**
771   * Extract information about available bands to set the bandcount and the
772   * processing directive
773   */
774  char nBands[2];
775  int nBandsI=GDALGetRasterCount( hDataset );
776  sprintf(nBands,"%d",GDALGetRasterCount( hDataset ));
777  msInsertHashTable(&(myLayer->metadata), "ows_bandcount", nBands);
778  if(nBandsI>=3)
779    msLayerAddProcessing(myLayer,"BANDS=1,2,3");
780  else if(nBandsI>=2)
781    msLayerAddProcessing(myLayer,"BANDS=1,2");
782  else
783    msLayerAddProcessing(myLayer,"BANDS=1");
784
785  /**
786   * Name available Bands
787   */
788  char lBands[6];
789  char *nameBands=NULL;
790  for( iBand = 0; iBand < nBandsI; iBand++ ){
791    sprintf(lBands,"Band%d",iBand+1);
792    if(nameBands==NULL){
793      nameBands=(char*)malloc((strlen(lBands)+1)*sizeof(char));
794      sprintf(nameBands,"%s",lBands);
795    }else{
796      if(iBand<4){
797        char *tmpS=zStrdup(nameBands);
798        nameBands=(char*)realloc(nameBands,(strlen(nameBands)+strlen(lBands)+1)*sizeof(char));
799        sprintf(nameBands,"%s %s",tmpS,lBands);
800        free(tmpS);
801      }
802    }
803  }
804  msInsertHashTable(&(myLayer->metadata), "ows_bandnames", nameBands);
805 
806  /**
807   * Loops over metadata informations to setup specific informations
808   */
809  for( iBand = 0; iBand < nBandsI; iBand++ ){
810    //int         bGotNodata;//, bSuccess;
811    double      adfCMinMax[2]/*, dfNoData*/;
812    //int         nBlockXSize, nBlockYSize, nMaskFlags;
813    //double      /*dfMean, dfStdDev*/;
814    hBand = GDALGetRasterBand( hDataset, iBand+1 );
815
816    CPLErrorReset();
817    GDALComputeRasterMinMax( hBand, FALSE, adfCMinMax );
818    char tmpN[21];
819    sprintf(tmpN,"Band%d",iBand+1);
820    if (CPLGetLastErrorType() == CE_None){
821      char tmpMm[100];
822      sprintf(tmpMm,"%.3f %.3f",adfCMinMax[0],adfCMinMax[1]);
823      char tmpI[21];
824      sprintf(tmpI,"%s_interval",tmpN);
825      msInsertHashTable(&(myLayer->metadata), tmpI, tmpMm);
826
827      map* test=getMap(output->content,"msClassify");
828      if(test!=NULL && strncasecmp(test->value,"true",4)==0){
829        /**
830         * Classify one band raster pixel value using regular interval
831         */
832        int _tmpColors[10][3]={
833          {102,153,204},
834          {51,102,153},
835          {102,102,204},
836          {51,204,0},
837          {153,255,102},
838          {204,255,102},
839          {102,204,153},
840          {255,69,64},
841          {255,192,115},
842          {255,201,115}
843        };
844         
845        if(nBandsI==1){
846          double delta=adfCMinMax[1]-adfCMinMax[0];
847          double interval=delta/10;
848          double cstep=adfCMinMax[0];
849          for(i=0;i<10;i++){
850            /**
851             * Create a new class
852             */
853            if(msGrowLayerClasses(myLayer) == NULL)
854              return -1;
855            if(initClass((myLayer->CLASS[myLayer->numclasses])) == -1)
856              return -1;
857            myLayer->CLASS[myLayer->numclasses]->type = myLayer->type;
858            if(msGrowClassStyles(myLayer->CLASS[myLayer->numclasses]) == NULL)
859              return -1;
860            if(initStyle(myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]) == -1)
861              return -1;
862           
863            /**
864             * Set class name
865             */
866            char className[7];
867            sprintf(className,"class%d",i);
868            myLayer->CLASS[myLayer->numclasses]->name=zStrdup(className);
869           
870            /**
871             * Set expression
872             */
873            char expression[1024];
874            if(i+1<10)
875              sprintf(expression,"([pixel]>=%.3f AND [pixel]<%.3f)",cstep,cstep+interval);
876            else
877              sprintf(expression,"([pixel]>=%.3f AND [pixel]<=%.3f)",cstep,cstep+interval);
878            msLoadExpressionString(&myLayer->CLASS[myLayer->numclasses]->expression,expression);
879           
880            /**
881             * Set color
882             */
883            myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->color.red=_tmpColors[i][0];
884            myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->color.green=_tmpColors[i][1];
885            myLayer->CLASS[myLayer->numclasses]->styles[myLayer->CLASS[myLayer->numclasses]->numstyles]->color.blue=_tmpColors[i][2];
886            cstep+=interval;
887            myLayer->CLASS[myLayer->numclasses]->numstyles++;
888            myLayer->numclasses++;
889           
890          }
891         
892          char tmpMm[100];
893          sprintf(tmpMm,"%.3f %.3f",adfCMinMax[0],adfCMinMax[1]);
894         
895        }
896      }
897    }
898    if( strlen(GDALGetRasterUnitType(hBand)) > 0 ){
899      char tmpU[21];
900      sprintf(tmpU,"%s_band_uom",tmpN);
901      msInsertHashTable(&(myLayer->metadata), tmpU, GDALGetRasterUnitType(hBand));
902    }
903
904  }
905
906  m->layerorder[m->numlayers] = m->numlayers;
907  m->numlayers++;
908  GDALClose( hDataset );
909  GDALDestroyDriverManager();
910  CPLCleanupTLS();
911  return 1;
912}
913
914/**
915 * Create a MapFile for WMS, WFS or WCS Service output
916 */
917void outputMapfile(maps* conf,maps* outputs){
918
919  /**
920   * Firs store the value on disk
921   */
922  map* mime=getMap(outputs->content,"mimeType");
923  char *ext="data";
924  if(mime!=NULL)
925    if(strncasecmp(mime->value,"application/json",16)==0)
926      ext="json";
927
928  map* tmpMap=getMapFromMaps(conf,"main","dataPath");
929  map* sidMap=getMapFromMaps(conf,"lenv","usid");
930  char *pszDataSource=(char*)malloc((strlen(tmpMap->value)+strlen(sidMap->value)+strlen(outputs->name)+17)*sizeof(char));
931  sprintf(pszDataSource,"%s/ZOO_DATA_%s_%s.%s",tmpMap->value,outputs->name,sidMap->value,ext); 
932  int f=zOpen(pszDataSource,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
933  map *gfile=getMap(outputs->content,"generated_file");
934  if(gfile!=NULL){
935    readGeneratedFile(conf,outputs->content,gfile->value);         
936  }
937  map* sizeMap=getMap(outputs->content,"size");
938  map* vData=getMap(outputs->content,"value");
939  if(sizeMap!=NULL){
940    zWrite(f,vData->value,atoi(sizeMap->value)*sizeof(char));
941  }
942  else{
943    zWrite(f,vData->value,(strlen(vData->value)+1)*sizeof(char));
944  }
945  close(f);
946  addToMap(outputs->content,"storage",pszDataSource);
947  free(pszDataSource);
948
949  /*
950   * Create an empty map, set name, default size and extent
951   */
952  mapObj *myMap=msNewMapObj();
953  free(myMap->name);
954  myMap->name=zStrdup("ZOO-Project_WXS_Server");
955  msMapSetSize(myMap,2048,2048);
956  msMapSetExtent(myMap,-1,-1,1,1);
957 
958  /*
959   * Set imagepath and imageurl using tmpPath and tmpUrl from main.cfg
960   */
961  map *tmp1=getMapFromMaps(conf,"main","tmpPath");
962  myMap->web.imagepath=zStrdup(tmp1->value);
963  tmp1=getMapFromMaps(conf,"main","tmpUrl");
964  myMap->web.imageurl=zStrdup(tmp1->value);
965 
966  /*
967   * Define supported output formats
968   */
969  outputFormatObj *o1=msCreateDefaultOutputFormat(NULL,"AGG/PNG","png");
970  o1->imagemode=MS_IMAGEMODE_RGBA;
971  o1->transparent=MS_TRUE;
972  o1->inmapfile=MS_TRUE;
973  msAppendOutputFormat(myMap,msCloneOutputFormat(o1));
974  msFreeOutputFormat(o1);
975
976#ifdef USE_KML
977  outputFormatObj *o2=msCreateDefaultOutputFormat(NULL,"KML","kml");
978  o2->inmapfile=MS_TRUE; 
979  msAppendOutputFormat(myMap,msCloneOutputFormat(o2));
980  msFreeOutputFormat(o2);
981#endif
982
983  outputFormatObj *o3=msCreateDefaultOutputFormat(NULL,"GDAL/GTiff","tiff");
984  if(!o3)
985    fprintf(stderr,"Unable to initialize GDAL driver !\n");
986  else{
987    o3->imagemode=MS_IMAGEMODE_BYTE;
988    o3->inmapfile=MS_TRUE; 
989    msAppendOutputFormat(myMap,msCloneOutputFormat(o3));
990    msFreeOutputFormat(o3);
991  }
992
993  outputFormatObj *o4=msCreateDefaultOutputFormat(NULL,"GDAL/AAIGRID","grd");
994  if(!o4)
995    fprintf(stderr,"Unable to initialize GDAL driver !\n");
996  else{
997    o4->imagemode=MS_IMAGEMODE_INT16;
998    o4->inmapfile=MS_TRUE; 
999    msAppendOutputFormat(myMap,msCloneOutputFormat(o4));
1000    msFreeOutputFormat(o4);
1001  }
1002
1003#ifdef USE_CAIRO
1004  outputFormatObj *o5=msCreateDefaultOutputFormat(NULL,"CAIRO/PNG","cairopng");
1005  if(!o5)
1006    fprintf(stderr,"Unable to initialize CAIRO driver !\n");
1007  else{
1008    o5->imagemode=MS_IMAGEMODE_RGBA;
1009    o5->transparent=MS_TRUE;
1010    o5->inmapfile=MS_TRUE;
1011    msAppendOutputFormat(myMap,msCloneOutputFormat(o5));
1012    msFreeOutputFormat(o5);
1013  }
1014#endif
1015
1016  /*
1017   * Set default projection to EPSG:4326
1018   */
1019  msLoadProjectionStringEPSG(&myMap->projection,"EPSG:4326");
1020  myMap->transparent=1;
1021
1022  /**
1023   * Set metadata extracted from main.cfg file maps
1024   */
1025  maps* cursor=conf;
1026  map* correspondance=getCorrespondance();
1027  while(cursor!=NULL){
1028    map* _cursor=cursor->content;
1029    map* vMap;
1030    while(_cursor!=NULL){
1031      if((vMap=getMap(correspondance,_cursor->name))!=NULL){
1032        if (msInsertHashTable(&(myMap->web.metadata), vMap->value, _cursor->value) == NULL){
1033#ifdef DEBUGMS
1034          fprintf(stderr,"Unable to add metadata");
1035#endif
1036          return;
1037        }
1038      }
1039      _cursor=_cursor->next;
1040    }
1041    cursor=cursor->next;
1042  }
1043  freeMap(&correspondance);
1044  free(correspondance);
1045
1046  /*
1047   * Set mapserver PROJ_LIB/GDAL_DATA or any other config parameter from
1048   * the main.cfg [mapserver] section if any
1049   */
1050  maps *tmp3=getMaps(conf,"mapserver");
1051  if(tmp3!=NULL){
1052    map* tmp4=tmp3->content;
1053    while(tmp4!=NULL){
1054      msSetConfigOption(myMap,tmp4->name,tmp4->value);
1055      tmp4=tmp4->next;
1056    }
1057  }
1058
1059  /**
1060   * Set a ows_rootlayer_title, 
1061   */
1062  if (msInsertHashTable(&(myMap->web.metadata), "ows_rootlayer_name", "ZOO_Project_Layer") == NULL){
1063#ifdef DEBUGMS
1064    fprintf(stderr,"Unable to add metadata");
1065#endif
1066    return;
1067  }
1068  if (msInsertHashTable(&(myMap->web.metadata), "ows_rootlayer_title", "ZOO_Project_Layer") == NULL){
1069#ifdef DEBUGMS
1070    fprintf(stderr,"Unable to add metadata");
1071#endif
1072    return;
1073  }
1074
1075  /**
1076   * Enable all the WXS requests using ows_enable_request
1077   * see http://mapserver.org/trunk/development/rfc/ms-rfc-67.html
1078   */
1079  if (msInsertHashTable(&(myMap->web.metadata), "ows_enable_request", "*") == NULL){
1080#ifdef DEBUGMS
1081    fprintf(stderr,"Unable to add metadata");
1082#endif
1083    return;
1084  }
1085  msInsertHashTable(&(myMap->web.metadata), "ows_srs", "EPSG:4326");
1086
1087  if(tryOgr(conf,outputs,myMap)<0)
1088    if(tryGdal(conf,outputs,myMap)<0)
1089      return ;
1090
1091  tmp1=getMapFromMaps(conf,"main","dataPath");
1092  char *tmpPath=(char*)malloc((13+strlen(tmp1->value))*sizeof(char));
1093  sprintf(tmpPath,"%s/symbols.sym",tmp1->value);
1094  msInitSymbolSet(&myMap->symbolset);
1095  myMap->symbolset.filename=zStrdup(tmpPath);
1096  free(tmpPath);
1097
1098  map* sid=getMapFromMaps(conf,"lenv","usid");
1099  char *mapPath=
1100    (char*)malloc((7+strlen(sid->value)+strlen(outputs->name)+strlen(tmp1->value))*sizeof(char));
1101  sprintf(mapPath,"%s/%s_%s.map",tmp1->value,outputs->name,sid->value);
1102  msSaveMap(myMap,mapPath);
1103  free(mapPath);
1104  msGDALCleanup();
1105  msFreeMap(myMap);
1106}
1107
1108#endif
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