Changes between Version 6 and Version 7 of ZooWorkshop/FOSS4GJapan/CreatingOGRBasedWebServices


Ignore:
Timestamp:
Oct 15, 2010, 5:35:57 PM (9 years ago)
Author:
djay
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ZooWorkshop/FOSS4GJapan/CreatingOGRBasedWebServices

    v6 v7  
    137137
    138138[[Image(Practical introduction to ZOO - 6.png,width=550px)]]
     139
     140== Implementing single geometry processes ==
     141
     142In order to learn the Services Provider creation and deployement step-by-step, we will first focus on creating a very simple one dedicated to the Boundary function. Similar procedure will then be used for the Buffer, Centroid and ConvexHull implementation.
     143
     144Your metadata is now ok, so you now must create the code of your Service. The most important thing you must be aware of when coding ZOO Services is that the function corresponding to your Service takes three parameters (internal maps datatype or Python dictionaries) and returns an integer value representing the status of execution (SERVICE_FAILED or SERVICE_SUCCEEDED) :
     145
     146 * conf : The main environment configuration (corresponding to the main.cfg content)
     147 * inputs : The requested / default inputs
     148 * outputs : The requested / default outputs
     149
     150=== Boundary ===
     151
     152==== C Version ====
     153
     154As explained before, ZOO Kernel will pass the parameters to your Service function in a specific datatype called maps. In order to code your Service in C language, you also need to learn how to access this datatype in read/write mode.
     155
     156The maps are simple map named linked list containing a name, a content map and a pointer to the next map in the list (or NULL if there is no more map in the list). Here is the datatype definition as you can find in the zoo-kernel/service.h file :
     157
     158{{{
     159#!c
     160typedef struct maps{
     161    char* name;
     162    struct map* content;
     163    struct maps* next;
     164} maps;
     165}}}
     166
     167The map included in the maps is also a simple linked list and is used to store Key Value Pair values. Amap is thus a couple of name and value and a pointer to the next element in the list. Here is the datatype definition you can find in the zoo-kernel/service.h file :
     168
     169{{{
     170#!c
     171typedef struct map{
     172    char* name;         /* The key */
     173    char* value;         /* The value */
     174    struct map* next; /* Next couple */
     175} map;
     176}}}
     177
     178As partially or fully filled datastructures will be passed by the ZOO Kernel to your Services, this means that you do not need to deal with maps creation but directly with existing map, in other words the content of each maps. The first function you need to know is getMapFromMaps (defined in the zoo-kernel/service.h file) which let you access to a specific map of a maps.
     179
     180This function takes three parameters listed bellow :
     181
     182 * m : a maps pointer representing the maps used to search the specific map
     183 * name : a char* representing the name of the map you are searching for
     184 * key : a specific key in the map named name
     185
     186For example, the following syntax will be used to access the InputPolygon value map of a maps named inputs, your C code should be :
     187
     188{{{
     189#!c
     190map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     191}}}
     192
     193Once you get the map, you can access the name or the value fields, using the following syntax :
     194
     195{{{
     196#!c
     197tmp->name
     198tmp->value
     199}}}
     200
     201As you know how to read and access the map fields from a maps, you can now learn how to write in such a datastructure. This is done by using the simple addToMap function once again defined in zoo- kernel/service.h. The addToMap function also takes three parameters :
     202
     203 * m : a map pointer you want to update,
     204 * n : the name of the map you want to add or update the value,
     205 * v : the value you want to set for this map.
     206
     207Here is an example of how to add or edit the content map of a maps called outputs :
     208
     209{{{
     210#!c
     211addToMap(outputs->content,"value","Hello from the C World !");
     212addToMap(outputs->content,"mimeType","text/plain");
     213addToMap(outputs->content,"encoding","UTF-8");
     214}}}
     215
     216Please note that the addToMap function is able to create or update an existing map. Indeed, if a map called « value » allready exists, then its value will be updated automatically.
     217
     218This datatype is really important cause it is used in every C based ZOO Services. It is also the same representation used in other languages but using their respectives datatypes. For Example in Python, the dictionaries datatype is used, so manipulation is much easier.
     219
     220Here is an example of a maps used in Python language (this is a summarized version of the main configaration maps) :
     221
     222{{{
     223#!python
     224main={
     225  "main": {
     226    "encoding": "utf-8",
     227    "version": "1.0.0",
     228    "serverAddress": "http://www.zoo-project.org/zoo/",
     229    "lang": "fr-FR,en-CA"
     230  },
     231  "identification": {"title": "The Zoo WPS Development Server",
     232    "abstract": "Development version of ZooWPS.",
     233    "fees": "None",
     234    "accessConstraints": "none",
     235    "keywords": "WPS,GIS,buffer"
     236  }
     237}
     238}}}
     239
     240As you know how to deal with maps and map, you are ready to code the first ZOO Service by using the OGR Boundary function.
     241
     242As already said in introduction we will use the Geoserver WFS server available on OSGeoLive, so full WFS Response will be used as inputs values. As we will use the simple OGR Geometry functions like OGR_G_GetBoundary, only the Geometry object will be used rather than a full WFS Response. The first thing to do is to write a function which will extract the geometry definition from the full WFS Response. We will call it createGeometryFromWFS.
     243
     244Here is the code of such a function :
     245
     246{{{
     247#!c
     248OGRGeometryH createGeometryFromWFS(maps* conf,char* inputStr){
     249  xmlInitParser();
     250  xmlDocPtr doc = xmlParseMemory(inputStr,strlen(inputStr));
     251  xmlChar *xmlbuff;
     252  int buffersize;
     253  xmlXPathContextPtr xpathCtx;
     254  xmlXPathObjectPtr xpathObj;
     255  char * xpathExpr="/*/*/*/*/*[local-name()='Polygon' or local-name()='MultiPolygon']";
     256  xpathCtx = xmlXPathNewContext(doc);
     257  xpathObj = xmlXPathEvalExpression(BAD_CAST xpathExpr,xpathCtx);
     258  if(!xpathObj->nodesetval){
     259    errorException(conf, "Unable to parse Input Polygon","InvalidParameterValue");
     260    exit(0);
     261  }
     262  int size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
     263  xmlDocPtr ndoc = xmlNewDoc(BAD_CAST "1.0");
     264  for(int k=size-1;k>=0;k--){
     265    xmlDocSetRootElement(ndoc, xpathObj->nodesetval->nodeTab[k]);
     266  }
     267  xmlDocDumpFormatMemory(ndoc, &xmlbuff, &buffersize, 1);
     268  char *tmp=strdup(strstr((char*)xmlbuff,"?>")+2);
     269  xmlXPathFreeObject(xpathObj);
     270  xmlXPathFreeContext(xpathCtx);
     271  xmlFree(xmlbuff);
     272  xmlFreeDoc(doc);
     273  xmlCleanupParser();
     274  OGRGeometryH res=OGR_G_CreateFromGML(tmp);
     275  if(res==NULL){
     276    map* tmp=createMap("text","Unable to call OGR_G_CreatFromGML");
     277    addToMap(tmp,"code","NoApplicableCode");
     278    exit(0);
     279  }
     280  else
     281    return res;
     282}
     283}}}
     284
     285The only thing we will focus on is the call to the errorException function used in the function body. This function is declared in the zoo-kernel/service_internal.h and defined in zoo- kernel/service_internal.c file. It takes three parameters as follow :
     286
     287 * the main environment maps,
     288 * a char* representing the error message to display,
     289 * a char* representing the error code (as defined in the WPS specification – Table 62).
     290
     291In other words, if the WFS response cannot be parsed properly, then you will return anExceptionReport document informing the client that a problem occured.
     292
     293The function to extract the geometry object from a WFS Response is written, so you can now start defining the Boundary Service. Here is the full code for the Boundary Service :
     294
     295{{{
     296#!c
     297int Boundary(maps*& conf,maps*& inputs,maps*& outputs){
     298  OGRGeometryH geometry,res;
     299  map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     300  if(tmp==NULL)
     301    return SERVICE_FAILED;
     302  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     303  if(strncmp(tmp1->value,"application/json",16)==0)
     304    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     305  else
     306    geometry=createGeometryFromWFS(conf,tmp->value);
     307  res=OGR_G_GetBoundary(geometry);
     308  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     309  if(strncmp(tmp1->value,"application/json",16)==0){
     310    addToMap(outputs->content,"value",OGR_G_ExportToJson(res));
     311    addToMap(outputs->content,"mimeType","text/plain");
     312  }
     313  else{
     314    addToMap(outputs->content,"value",OGR_G_ExportToGML(res));
     315  }
     316  outputs->next=NULL;
     317  OGR_G_DestroyGeometry(geometry);
     318  OGR_G_DestroyGeometry(res);
     319  return SERVICE_SUCCEEDED;
     320}
     321}}}
     322
     323As you can see in the code above, the mimeType of the data inputs passed to our Service is first checked :
     324
     325{{{
     326#!c
     327  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     328  if(strncmp(tmp1->value,"application/json",16)==0)
     329    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     330  else
     331    geometry=createGeometryFromGML(conf,tmp->value);
     332}}}
     333
     334Basicaly, if we get an input with a mimeType set to application/json, then we will use our OGR_G_CreateGeometryFromJson in other case, our createGeometryFromGML local function.
     335
     336Please note that in some sense the data inputs are not really of the same kind. Indeed as we used directly OGR_G_CreateGeometryFromJson it means that the JSON string include only the geometry object and not the full GeoJSON string. Nevertheless, you can easily change this code to be able to use a full GeoJSON string, simply by creating a function which will extract the geometry object from the GeoJSON string (using the json-c library for instance, which is also used by the OGR GeoJSON Driver).
     337
     338Once you can access the input geometry object, you can use the OGR_G_GetBoundary function and store the result in the res geometry variable. Then, you only have to store the value in the right format : GeoJSON per default or GML as we declared it as a supported output format.
     339
     340Please note that ZOO Kernel will give you pre-filled outputs values, so you will only have to fill the value for the key named value, even if in our example we override the mimeType using the text/plain value rather than the application/json (to show that we can also edit other fields of a map). Indeed, depending on the format requested by the client (or the default one) we will provide JSON or GML representation of the geometry.
     341
     342{{{
     343#!c
     344  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     345  if(strncmp(tmp1->value,"application/json",16)==0){
     346    addToMap(outputs->content,"value",OGR_G_ExportToJson(res));
     347    addToMap(outputs->content,"mimeType","text/plain");
     348  }
     349  else{
     350    addToMap(outputs->content,"value",OGR_G_ExportToGML(res));
     351  }
     352}}}
     353
     354The Boundary ZOO Service is now implemented and you need to compile it to produce a Shared Library. As you just used functions defined in service.h (getMapFromMaps and addToMap), you must include this file in your C code. The same requirement is needed to be able to use the errorException function declared in zoo-kernel/service_internal.h, you also must link your service object file to the zoo- kernel/service_internal.o in order to use errorException on runtime. You must then include the required files to access the libxml2 and OGR C-API.
     355
     356For the need of the Shared Library, you have to put your code in a block declared asextern "C". The final Service code should be stored in a service.c file located in the root of the Services Provider directory (so in /home/zoows/sources/zoo-services/ws_sp). It should look like this :
     357
     358{{{
     359#!c
     360#include "ogr_api.h"
     361#include "service.h"
     362extern "C" {
     363#include <libxml/tree.h>
     364#include <libxml/parser.h>
     365#include <libxml/xpath.h>
     366#include <libxml/xpathInternals.h>
     367<YOUR SERVICE CODE AND OTHER UTILITIES FUNCTIONS>
     368}
     369}}}
     370
     371The full source code of your Service is now ready and you must produce the corresponding Service Shared Object by compiling the code as a Shared Library. This can be done using the following command:
     372
     373{{{
     374#!c
     375g++ $CFLAGS -shared -fpic -o cgi-env/ServiceProvider.zo ./service.c $LDFLAGS
     376}}}
     377
     378Please note that the CLFAGS and LDFLAGS environment variables values must be set before.
     379
     380The CFLAGS must contain all the requested paths to find included headers, so the path to the directories where the ogr_api.h, libxml2 directory, service.h and service_internal.h files are located. Thanks to the OSGeoLive environment, some of the provided tools can be used to retrieve those values : xml2-config and gdal-config, both used with the --cflags argument. They will produce the desired paths for you.
     381
     382If you follow the instructions to create your ZOO Services Provider main directory inzoo-services, then you should find the ZOO Kernel headers and source tree which is located in the../../zoo-kernel directory relatively to your current path (/home/user/zoows/sources/zoo-services/ws_sp). Note that you can also use a full path to the zoo-kernel directory but using relative path will let you move your sources tree somewhere else and keep your code compiling using exactly the same command line. So you must add a -I../../zoo-kernel to your CFLAGS to make the compiler able to find the service.h and service_internal.h files.
     383
     384The full CFLAGS definition should look like this :
     385
     386{{{
     387CFLAGS=`gdal-config --cflags` `xml2-config --clfags` -I../../zoo-kernel/
     388}}}
     389
     390Once you get the included paths correctly set in your CFLAGS , it is time to concentrate on the library we have to link against (defined in the LDFLAGS env variable). In order to link against the gdal and libxml2 libraries, you can use the same tools than above using the --libs argument rather than --cflags. The full LDFLAGS definition must look like this :
     391
     392{{{
     393#!c
     394LDFLAGS=`gdal-config --libs` `xml2-config --libs` ../../zoo-kernel/service_internal.o
     395}}}
     396
     397Let's now create a Makefile which will help you compiling your code over the time. Please write a short Makefile in the root of your ZOO Services Provider directory, containing the following lines :
     398
     399{{{
     400ZOO_SRC_ROOT=../../zoo-kernel/
     401CFLAGS=-I${ZOO_SRC_ROOT} `xml2-config --cflags` `gdal-config --cflags`
     402LDFLAGS=`xml2-config --libs` `gdal-config --libs`${ZOO_SRC_ROOT}/service_internal.o
     403
     404cgi-env/ogr_ws_service_provider.zo: service.c
     405    g++ ${CFLAGS} -shared -fpic -o cgi-env/ogr_ws_service_provider.zo ./service.c $ {LDFLAGS}
     406clean:
     407    rm -f cgi-env/ogr_ws_service_provider.zo
     408}}}
     409
     410Using this Makefile, you should be able to run make from your ZOO Service Provider main directory and to get the resulting ogr_ws_service_provider.zo file located in the cgi-env directory.
     411
     412The metadata file and the ZOO Service Shared Object are now both located in the cgi-env directory. In order to deploy your new ServiceProvider, you only have to copy the ZOO Service Shared Object and its corresponding metadata file in the directory where ZOO Kernel is located, so in/usr/lib/cgi-bin. You must use a sudo command to achieve this task:
     413
     414{{{
     415#!sh
     416sudo cp ./cgi-env/* /usr/lib/cgi-bin
     417}}}
     418
     419You should now understand more clearly the meannings of the ZOO Service Provider source tree ! Thecgi- env directory will let you deploy your new Services or Services Provider in an easy way , simply by copying the whole cgi-env content in your cgi-bin directory.
     420
     421Please note that you can add the following lines to your Makefile to be able to type make install directly and to get your new Services Provider available for use from ZOO Kernel :
     422
     423{{{
     424install:
     425    sudo cp ./cgi-env/* /usr/lib/cgi-bin
     426}}}
     427
     428Your ZOO Services Provider is now ready to use from an Execute request passed to ZOO Kernel.
     429
     430==== Python Version ====

Search

Context Navigation

ZOO Sponsors

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

Become a sponsor !

Knowledge partners

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

Become a knowledge partner

Related links

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