source: trunk/zoo-project/zoo-kernel/service_internal_otb.c @ 621

Last change on this file since 621 was 619, checked in by djay, 9 years ago

Fix issue with OTB multiple inputs potentially encoded in base64.

  • Property svn:keywords set to Id
File size: 19.0 KB
Line 
1/*
2 * Author : Gérald FENOY
3 *
4 * Copyright (c) 2015 GeoLabs SARL
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 * See Ref: http://hg.orfeo-toolbox.org/OTB/ Copyright
25 * Some parts of this code are derived from ITK. See ITKCopyright.txt for
26 * details.
27 */
28
29#include "service_internal_otb.h"
30
31using namespace otb::Wrapper;
32
33/**
34 * The ZooWatcher list
35 */
36WatcherListType m_WatcherList;
37/**
38 * A pointer to the conf maps containing the main.cfg settings
39 */
40maps* m_Conf;
41
42/**
43 * The command to create a ZooWatcher and add it to the global m_WatcherList
44 */
45class MyCommand : public itk::Command
46{
47 public:
48  itkNewMacro( MyCommand );
49 public:
50
51  /**
52   * The method that defines the action to be taken by the command.
53   *
54   * @param caller an itk::Object pointer
55   * @param event an itk::EventObject pointer
56   */
57  void Execute(itk::Object *caller, const itk::EventObject & event)
58  {
59    Execute( (const itk::Object *)caller, event);
60  }
61 
62  /**
63   * The method that defines the action to be taken by the command.
64   * Create a new ZooWatcher instance then add it to the m_WatcherList.
65   *
66   * @param caller a const itk::Object pointer
67   * @param event an itk::EventObject pointer
68   * @see ZooWatcher,ZooWatcher::SetConf
69   */
70  void Execute(const itk::Object *caller, const itk::EventObject & event)
71  {
72    const AddProcessToWatchEvent* eventToWatch = dynamic_cast< const AddProcessToWatchEvent*> ( &event );
73    std::string m_CurrentDescription = eventToWatch->GetProcessDescription();
74    ZooWatcher * watch = new ZooWatcher(eventToWatch->GetProcess(),
75                                        eventToWatch->GetProcessDescription());
76    watch->SetConf(&m_Conf);
77    m_WatcherList.push_back(watch);
78  }
79
80};
81
82/**
83 * Replace all occurence of from by to in a str string
84 *
85 * @param str the string to transform
86 * @param from the string to replace
87 * @param to the string used as replacement
88 * @return the resulting string
89 */
90std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
91    size_t start_pos = 0;
92    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
93        str.replace(start_pos, from.length(), to);
94        start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
95    }
96    return str;
97}
98
99/**
100 * Write a file from value and length
101 *
102 * @param fname the file name
103 * @param val the value
104 * @param length the value length
105 */
106int writeFile(char* fname,char* val,int length){
107  FILE* of=fopen(fname,"wb");
108  if(of==NULL){
109    return -1;
110  }
111  size_t ret=fwrite(val,sizeof(char),length,of);
112  if(ret<length){
113    fprintf(stderr,"Write error occured!\n");
114    fclose(of);
115    return -1;
116  }
117  fclose(of);
118  return 1;
119}
120
121/**
122 * Load and run an OTB Application corresponding to the service by using inputs parameters.
123 * Define the m_Conf
124 *
125 * @param main_conf the conf maps containing the main.cfg settings
126 * @param request the map containing the HTTP request
127 * @param s the service structure
128 * @param real_inputs the maps containing the inputs
129 * @param real_outputs the maps containing the outputs
130 */
131int zoo_otb_support(maps** main_conf,map* request,service* s,maps **real_inputs,maps **real_outputs){
132  maps* m=*main_conf;
133  maps* inputs=*real_inputs;
134  maps* outputs=*real_outputs;
135  map* tmp0=getMapFromMaps(*main_conf,"lenv","cwd");
136  char *ntmp=tmp0->value;
137  map* tmp=NULL;
138  int res=-1;
139
140  std::vector<std::string> list = ApplicationRegistry::GetAvailableApplications();
141  if (list.size() == 0){
142    map* tmps=createMap("text","No OTB Application found.");
143    addToMap(tmps,"code","InternalError");
144    printExceptionReportResponse(m,tmps);
145    freeMap(&tmps);
146    free(tmps);
147    res=-1;
148  }
149  else{
150    for (std::vector<std::string>::const_iterator it = list.begin(); it != list.end(); ++it){
151      if(s->name==*it){
152        Application::Pointer m_Application=ApplicationRegistry::CreateApplication(*it);
153        if (m_Application.IsNull()){
154          char tmpS[1024];
155          sprintf(tmpS, "The OTB Application %s cannot be loaded.", (*it).c_str());
156          map* tmps=createMap("text",tmpS);
157          addToMap(tmps,"code","InternalError");
158          printExceptionReportResponse(m,tmps);
159          freeMap(&tmps);
160          free(tmps);
161          res=-1;
162        }else{
163          // Create Observer on AddProcessToWatchEvent
164          m_Conf=m;
165          MyCommand::Pointer myCommand = MyCommand::New();
166          m_Application->AddObserver(AddProcessToWatchEvent(), myCommand);
167          char tmpS[1024];
168          const std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
169          for (unsigned int i = 0; i < appKeyList.size(); i++){
170            const std::string paramKey(appKeyList[i]);
171            std::vector<std::string> values;
172            Parameter::Pointer param = m_Application->GetParameterByKey(paramKey);
173            ParameterType type = m_Application->GetParameterType(paramKey);
174            if (type != ParameterType_Group && paramKey!="inxml" && paramKey!="outxml"){
175              map* test=getMapFromMaps(inputs,paramKey.c_str(),"cache_file");
176              if(test==NULL){
177                test=getMapFromMaps(inputs,paramKey.c_str(),"inRequest");
178                map* tmpPath=getMapFromMaps(m,"main","tmpPath");
179                map* tmpSid=getMapFromMaps(m,"lenv","usid");
180                char tmp[1024];
181                map* tmpVal=getMapFromMaps(outputs,paramKey.c_str(),"mimeType");
182                maps* tmpMaps=getMaps(outputs,paramKey.c_str());
183                if(test!=NULL && test->value!=NULL && strncasecmp(test->value,"true",4)==0){
184                  test=getMapFromMaps(inputs,paramKey.c_str(),"value");
185                  if(type == ParameterType_OutputImage){
186                    ImagePixelType outPixType = ImagePixelType_float;
187                    if (strncasecmp(test->value,"uint8",5)==0)
188                      outPixType = ImagePixelType_uint8;
189                    else if (strncasecmp(test->value,"int16",5)==0)
190                      outPixType = ImagePixelType_int16;
191                    else if (strncasecmp(test->value,"uint16",6)==0)
192                      outPixType = ImagePixelType_uint16;
193                    else if (strncasecmp(test->value,"int32",5)==0)
194                      outPixType = ImagePixelType_int32;
195                    else if (strncasecmp(test->value,"uint32",6)==0)
196                      outPixType = ImagePixelType_uint32;
197                    else if (strncasecmp(test->value,"double",6)==0)
198                      outPixType = ImagePixelType_double;
199                    const char* ext="tiff";
200                    if(tmpVal!=NULL){
201                      if(strncasecmp(tmpVal->value,"image/jp2",9)==0)
202                         ext="j2k";
203                      else
204                        if(strncasecmp(tmpVal->value,"image/png",9)==0)
205                         ext="png";
206                        else
207                          if(strncasecmp(tmpVal->value,"image/jpeg",10)==0)
208                            ext="jpeg";
209                    }
210                    sprintf(tmp,"%s/%s_%s.%s",tmpPath->value,s->name,tmpSid->value,ext);
211                    m_Application->SetParameterString(paramKey, tmp);
212                    setMapInMaps(inputs,paramKey.c_str(),"generated_file",tmp);
213                    dynamic_cast<OutputImageParameter *> (param.GetPointer())->SetPixelType(outPixType);
214                  }else{
215                    map* tmpPath=getMapFromMaps(m,"main","tmpPath");
216                    map* tmpSid=getMapFromMaps(m,"lenv","sid");
217                    map* tmpVal=getMapFromMaps(inputs,paramKey.c_str(),"mimeType");
218                    char file_ext[32];
219                    getFileExtension(tmpVal != NULL ? tmpVal->value : NULL, file_ext, 32);
220                    if(type == ParameterType_InputImageList){
221                      char *val=(char*)malloc((strlen(tmpPath->value)+strlen(s->name)+strlen(tmpSid->value)+strlen(file_ext)+13)*sizeof(char));
222                      sprintf(val,"%s/Input_%s_%s_%d.%s",tmpPath->value,s->name,tmpSid->value,0,file_ext);
223                      int length=0;
224                      map* tmpSize=getMap(test,"size");
225                      if(tmpSize!=NULL){
226                        length=atoi(tmpSize->value);
227                      }
228                      writeFile(val,test->value,length);
229                      values.push_back(val);
230                      free(val);
231                      map* tmpLength=getMapFromMaps(inputs,paramKey.c_str(),"length");
232                      if(tmpLength!=NULL){
233                        int len=atoi(tmpLength->value);
234                        maps* tmpI=getMaps(inputs,paramKey.c_str());
235                        for(int k=1;k<len;k++){
236                          val=(char*)malloc((strlen(tmpPath->value)+strlen(s->name)+strlen(tmpSid->value)+strlen(file_ext)+13)*sizeof(char));
237                          sprintf(val,"%s/Input_%s_%s_%d.%s",tmpPath->value,s->name,tmpSid->value,k,file_ext);
238                          length=0;
239                          map* tmpV=getMapArray(tmpI->content,"value",k);
240                          tmpSize=getMapArray(tmpI->content,"size",k);
241                          if(tmpSize!=NULL){
242                            length=atoi(tmpSize->value);
243                          }
244                          writeFile(val,tmpV->value,length);
245                          values.push_back(val);
246                          free(val);
247                        }
248                      }
249                      dynamic_cast<InputImageListParameter *> (param.GetPointer())->SetListFromFileName(values);
250                    }
251                    else
252                      if(type == ParameterType_InputVectorData || type == ParameterType_InputImage
253                           || type == ParameterType_ComplexInputImage || type == ParameterType_InputVectorData
254                           || type == ParameterType_InputFilename){
255                        char tmp[1024];
256                        char* ext="json";
257                        if(tmpVal!=NULL){
258                          char *val=(char*)malloc((strlen(tmpPath->value)+strlen(s->name)+strlen(tmpSid->value)+strlen(file_ext)+10)*sizeof(char));
259                          sprintf(val,"%s/Input_%s_%s.%s",tmpPath->value,s->name,tmpSid->value,file_ext);
260                          int length=0;
261                          map* tmpSize=getMap(test,"size");
262                          if(tmpSize!=NULL){
263                            length=atoi(tmpSize->value);
264                          }
265                          writeFile(val,test->value,length);
266
267                          if(strncasecmp(tmpVal->value,"application/zip",14)==0){
268
269                            char tmpName[1024];
270                            sprintf(tmpName,"/vsizip/%s",val);
271                            char **files=VSIReadDir(tmpName);
272                            int nFiles = CSLCount( files );
273                            char tmpSSName[1024];
274                            sprintf(tmpSSName,"%s/Input_%s_%s",tmpPath->value,s->name,tmpSid->value);
275                            mkdir(tmpSSName,0777);
276                           
277                            char tmpSName[1024];
278                            for(int kk=0;kk<nFiles;kk++){
279                              sprintf(tmpSName,"%s/%s",tmpName,files[kk]);
280                              VSILFILE* fmain=VSIFOpenL(tmpSName, "rb");
281                              if(fmain!=NULL){
282                                VSIFSeekL(fmain,0,SEEK_END);
283                                long count=VSIFTellL(fmain);
284                                VSIRewindL(fmain);
285                               
286                                char *content=(char*) malloc((count+1)*sizeof(char)); 
287                                VSIFReadL(content,1,count*sizeof(char),fmain);
288                               
289                                char tmpSSSName[1024];
290                                sprintf(tmpSSSName,"%s/%s",tmpSSName,files[kk]);
291                               
292                                FILE* fx=fopen(tmpSSSName, "wb");
293                                fwrite(content,1,count,fx);
294                                fclose(fx);
295                                VSIFCloseL(fmain);
296                                free(content);
297                                std::string test1(tmpSSSName);
298                                if(test1.find(".shp")!=std::string::npos){
299                                  setMapInMaps(inputs,paramKey.c_str(),"cache_file",tmpSSSName);
300                                  test=getMapFromMaps(inputs,paramKey.c_str(),"cache_file");
301                                }
302                              }
303                            }
304                            m_Application->SetParameterString(paramKey, test->value);
305                          }else{
306                            m_Application->SetParameterString(paramKey, val);
307                          }
308                          free(val);
309                        }
310                      }
311                      else
312                        if(test->value!=NULL)
313                          m_Application->SetParameterString(paramKey, test->value);
314                  }
315
316                }else{
317                  if(type == ParameterType_OutputVectorData){
318                      char* ext="json";
319                      if(tmpVal!=NULL){
320                        if(strncasecmp(tmpVal->value,"text/xml",8)==0)
321                        ext="gml";
322                      else
323                        if(strncasecmp(tmpVal->value,"applicaton/json",15)==0)
324                          ext="json";
325                        else
326                          if(strncasecmp(tmpVal->value,"application/zip",14)==0)
327                            ext="shp";
328                          else
329                            if(strncasecmp(tmpVal->value,"application/vnd.google-earth.kml+xml",36)==0)
330                              ext="kml";
331                      }
332                      sprintf(tmp,"%s/%s_%s.%s",tmpPath->value,s->name,tmpSid->value,ext);
333                      m_Application->SetParameterString(paramKey, tmp);
334                      setMapInMaps(inputs,paramKey.c_str(),"generated_file",tmp);
335                  }
336                  else
337                    if(type == ParameterType_OutputFilename){
338                      char* ext="txt";
339                      if(tmpVal!=NULL){
340                        if(strncasecmp(tmpVal->value,"text/xml",8)==0)
341                          ext="xml";
342                        else
343                          if(strncasecmp(tmpVal->value,"text/csv",15)==0)
344                            ext="csv";
345                          else
346                            if(strncasecmp(tmpVal->value,"application/zip",14)==0)
347                              ext="shp";
348                            else
349                              if(strncasecmp(tmpVal->value,"application/vnd.google-earth.kml+xml",36)==0)
350                                ext="kml";
351                              else
352                                if(strncasecmp(tmpVal->value,"application/vnd.google-earth.kmz",32)==0){
353                                  ext="kmz";
354                                  sprintf(tmp,"%s/%s_%sxt.%s",tmpPath->value,s->name,tmpSid->value,ext);
355                                  m_Application->SetParameterString(paramKey, tmp);
356                                  setMapInMaps(outputs,paramKey.c_str(),"expected_generated_file",tmp);
357                                }
358
359                      }
360                      sprintf(tmp,"%s/%s_%s.%s",tmpPath->value,s->name,tmpSid->value,ext);
361                      m_Application->SetParameterString(paramKey, tmp);
362                      setMapInMaps(inputs,paramKey.c_str(),"generated_file",tmp);
363                    }
364
365                }
366              }else{
367                if(type == ParameterType_InputImageList){
368                  values.push_back(test->value);
369                  map* tmpPath=getMapFromMaps(inputs,paramKey.c_str(),"length");
370                  if(tmpPath!=NULL){
371                    int len=atoi(tmpPath->value);
372                    for(int k=1;k<len;k++){
373                      char tmp[15];
374                      sprintf(tmp,"cache_file_%d",k);
375                      map* tmpVal=getMapFromMaps(inputs,paramKey.c_str(),tmp);
376                      if(tmpVal!=NULL){
377                        values.push_back(tmpVal->value);
378                      }
379                    }
380                  }
381                  dynamic_cast<InputImageListParameter *> (param.GetPointer())->SetListFromFileName(values);
382                }
383                else
384                  if(type == ParameterType_InputVectorData || type == ParameterType_InputFilename){
385                    map* tmpPath=getMapFromMaps(m,"main","tmpPath");
386                    map* tmpSid=getMapFromMaps(m,"lenv","sid");
387                    char tmp[1024];
388                    map* tmpVal=getMapFromMaps(inputs,paramKey.c_str(),"mimeType");
389                    char* ext="json";
390                    if(tmpVal!=NULL){
391                      if(strncasecmp(tmpVal->value,"application/zip",14)==0){
392                        char tmpName[1024];
393                        symlink(test->value,ReplaceAll(test->value,".zca",".zip").c_str());
394                        sprintf(tmpName,"/vsizip/%s",ReplaceAll(test->value,".zca",".zip").c_str());
395                        char **files=VSIReadDir(tmpName);
396                        int nFiles = CSLCount( files );
397                        char tmpSSName[1024];
398                        sprintf(tmpSSName,"%s/Input_%s_%s",tmpPath->value,s->name,tmpSid->value);
399                        mkdir(tmpSSName,0777);
400                           
401                        char tmpSName[1024];
402                        for(int kk=0;kk<nFiles;kk++){
403                          sprintf(tmpSName,"%s/%s",tmpName,files[kk]);
404                          VSILFILE* fmain=VSIFOpenL(tmpSName, "rb");
405                          if(fmain!=NULL){
406                            VSIFSeekL(fmain,0,SEEK_END);
407                            long count=VSIFTellL(fmain);
408                            VSIRewindL(fmain);
409
410                            char *content=(char*) malloc((count+1)*sizeof(char)); 
411                            VSIFReadL(content,1,count*sizeof(char),fmain);
412                         
413                            char tmpSSSName[1024];
414                            sprintf(tmpSSSName,"%s/%s",tmpSSName,files[kk]);
415                           
416                            FILE* fx=fopen(tmpSSSName, "wb");
417                            fwrite(content,1,count,fx);
418                            fclose(fx);
419                            VSIFCloseL(fmain);
420                            free(content);
421                            std::string test1(tmpSSSName);
422                            if(test1.find(".shp")!=std::string::npos){
423                              setMapInMaps(inputs,paramKey.c_str(),"cache_file",tmpSSSName);
424                              test=getMapFromMaps(inputs,paramKey.c_str(),"cache_file");
425                            }
426                          }
427                        }
428                      }
429                    }
430                   
431                    m_Application->SetParameterString(paramKey, test->value);
432                  }
433                  else
434                    if(type == ParameterType_InputImage
435                       || type == ParameterType_ComplexInputImage || type == ParameterType_InputVectorData
436                       || type == ParameterType_InputFilename){
437                      m_Application->SetParameterString(paramKey, test->value);
438                  }
439              }
440            }
441            param->SetUserValue(true);
442            m_Application->UpdateParameters();
443          }
444
445          try{
446            if( m_Application->ExecuteAndWriteOutput() == 0 ){
447              std::vector< std::pair<std::string, std::string> > paramList;
448              paramList = m_Application->GetOutputParametersSumUp();
449              if(paramList.size()>0)
450                for( unsigned int i=0; i<paramList.size(); i++){
451                  setMapInMaps(outputs,paramList[i].first.c_str(),"value",paramList[i].second.c_str());
452                }
453              else{
454                const std::vector<std::string> appKeyList = m_Application->GetParametersKeys(true);
455                for (unsigned int i = 0; i < appKeyList.size(); i++){
456                  const std::string paramKey(appKeyList[i]);
457                  std::vector<std::string> values;
458                  Parameter::Pointer param = m_Application->GetParameterByKey(paramKey);
459                  ParameterType type = m_Application->GetParameterType(paramKey);
460                  if (type != ParameterType_Group && paramKey!="inxml" && paramKey!="outxml"
461                      && (type == ParameterType_OutputImage || type == ParameterType_OutputFilename
462                          || type == ParameterType_OutputVectorData ) ){
463                    if(type == ParameterType_OutputImage || type == ParameterType_OutputFilename || type == ParameterType_OutputVectorData){
464                      map* test=getMapFromMaps(outputs,paramKey.c_str(),"mimeType");
465                      if(test!=NULL && strncasecmp(test->value,"application/zip",15)==0){
466                       
467                        test=getMapFromMaps(inputs,paramKey.c_str(),"generated_file");
468                        char tmpName[1024];
469                        sprintf(tmpName,"/vsizip/%s",ReplaceAll(test->value,".shp",".zip").c_str());
470                        VSILFILE* fmain=VSIFOpenL(tmpName, "w");
471                        FILE * file;
472                        char *tmp;
473                        char tmpSName[1024];
474                        long count;
475                       
476                        char *exts[4];
477                        exts[0]=".shp";
478                        exts[1]=".shx";
479                        exts[2]=".dbf";
480                        exts[3]=".prj";
481                        for(int c=0;c<4;c++){
482                          sprintf(tmpSName,"%s/result%s",tmpName,exts[c]);
483                         
484                          file=fopen(ReplaceAll(test->value,".shp",exts[c]).c_str(),"rb");
485                          if(file!=NULL){
486                            fseek(file, 0, SEEK_END);
487                            count = ftell(file);
488                            rewind(file);
489                           
490                            tmp=(char*) malloc((count+1)*sizeof(char)); 
491                            fread(tmp,1,count*sizeof(char),file);
492                           
493                            VSILFILE* fx=VSIFOpenL(tmpSName, "wb");
494                            VSIFWriteL(tmp,1,count,fx);
495                            VSIFCloseL(fx);
496                            fclose(file);
497                            free(tmp);
498                          }
499                        }
500                       
501                        VSIFCloseL(fmain);
502                       
503                        FILE* file1=fopen(ReplaceAll(test->value,".shp",".zip").c_str(), "rb");
504                        fseek(file1, 0, SEEK_END);
505                        count=ftell(file1);
506                        rewind(file1);
507                       
508                        tmp=(char*) malloc((count+1)*sizeof(char)); 
509                        fread(tmp,1,count*sizeof(char),file1);
510                       
511                        file=fopen(ReplaceAll(test->value,".shp",".zip").c_str(),"wb");
512                        fwrite(tmp,1,count,file);
513                        fclose(file);
514                        free(tmp);
515                        fclose(file1);
516                        setMapInMaps(inputs,paramKey.c_str(),"generated_file",ReplaceAll(test->value,".shp",".zip").c_str());
517                      }
518                      test=getMapFromMaps(inputs,paramKey.c_str(),"generated_file");
519
520                      if(test!=NULL){
521                        setMapInMaps(outputs,paramKey.c_str(),"generated_file",test->value);
522                      }
523
524                    }
525                  }
526                }
527              }
528              res=3;
529              break;
530            }
531            else{
532              sprintf(tmpS, "The OTB Application %s cannot be run.", s->name);
533              setMapInMaps(m,"lenv","message",tmpS);
534              res=SERVICE_FAILED;
535            }
536          }
537          catch(std::exception& err){
538            setMapInMaps(m,"lenv","message",err.what());
539            return SERVICE_FAILED;
540           
541          }
542          catch(...){
543            setMapInMaps(m,"lenv","message","An unknown exception has been raised during application execution");
544            res=SERVICE_FAILED;
545          }
546          break;
547        }
548      }
549    }
550  }
551
552  for (unsigned int i = 0; i < m_WatcherList.size(); i++){
553    m_WatcherList[i]->FreeConf();
554    delete m_WatcherList[i];
555    m_WatcherList[i] = NULL;
556  }
557  m_WatcherList.clear();
558
559  return res;
560}
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