Changes between Version 8 and Version 9 of ZooWorkshop/FOSS4GJapan/CreatingOGRBasedWebServices


Ignore:
Timestamp:
Oct 15, 2010, 6:16:34 PM (9 years ago)
Author:
djay
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ZooWorkshop/FOSS4GJapan/CreatingOGRBasedWebServices

    v8 v9  
    571571
    572572== Creating Services for other functions (ConvexHull and Centroid) ==
     573
     574As the Boundary sample service code is available, you can now easily add ConvexHull and Centroid functions as they take exactly the same number of arguments : Only one geometry. The details for implementing and deploying the ConvexHull Service are provided bellow, and we will let you do the same thing for the Centroid one.
     575
     576=== C Version ===
     577
     578Please add first the following code to the service.c source code :
     579
     580{{{
     581#!c
     582int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){
     583  OGRGeometryH geometry,res;
     584  map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     585  if(tmp==NULL)
     586    return SERVICE_FAILED;
     587  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     588  if(strncmp(tmp1->value,"application/json",16)==0)
     589    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     590  else
     591    geometry=createGeometryFromWFS(conf,tmp->value);
     592  res=OGR_G_ConvexHull(geometry);
     593  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     594  if(strncmp(tmp1->value,"application/json",16)==0){
     595    addToMap(outputs->content,"value",OGR_G_ExportToJson(res));
     596    addToMap(outputs->content,"mimeType","text/plain");
     597  }
     598  else{
     599    addToMap(outputs->content,"value",OGR_G_ExportToGML(res));
     600  }
     601  outputs->next=NULL;
     602  OGR_G_DestroyGeometry(geometry);
     603  OGR_G_DestroyGeometry(res);
     604  return SERVICE_SUCCEEDED;
     605}
     606}}}
     607
     608This new code is exactly the same as for the Boundary Service. The only thing we modified is the line where the OGR_G_ConvexHull function is called (rather than the OGR_G_GetBoundary you used before). It is better to not copy and paste the whole function and find a more generic way to define your new Services as the function body will be the same in every case. The following generic function is proposed to make things simpler :
     609
     610{{{
     611#!c
     612int applyOne(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc) (OGRGeometryH)){
     613  OGRGeometryH geometry,res;
     614  map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     615  if(tmp==NULL)
     616    return SERVICE_FAILED;
     617  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     618  if(strncmp(tmp1->value,"application/json",16)==0)
     619    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     620  else
     621    geometry=createGeometryFromWFS(conf,tmp->value);
     622  res=(*myFunc)(geometry);
     623  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     624  if(strncmp(tmp1->value,"application/json",16)==0){
     625    addToMap(outputs->content,"value",OGR_G_ExportToJson(res));
     626    addToMap(outputs->content,"mimeType","text/plain");
     627  }
     628  else{
     629    addToMap(outputs->content,"value",OGR_G_ExportToGML(res));
     630  }
     631  outputs->next=NULL;
     632  OGR_G_DestroyGeometry(geometry);
     633  OGR_G_DestroyGeometry(res);
     634  return SERVICE_SUCCEEDED;
     635}
     636}}}
     637
     638Then, a function pointer called myFunc rather than the full function name can be used. This way we can re- implement our Boundary Service this way :
     639
     640{{{
     641#!c
     642int Boundary(maps*& conf,maps*& inputs,maps*& outputs){
     643  return applyOne(conf,inputs,outputs,&OGR_G_GetBoundary);
     644}
     645}}}
     646
     647Using this applyOne local function defined in the service.c source code, we can define other Services this way :
     648
     649{{{
     650#!c
     651int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){
     652  return applyOne(conf,inputs,outputs,&OGR_G_ConvexHull);
     653}
     654int Centroid(maps*& conf,maps*& inputs,maps*& outputs){
     655  return applyOne(conf,inputs,outputs,&MY_OGR_G_Centroid);
     656}
     657}}}
     658
     659The genericity of the applyOne function let you add two new Services in your ZOO Services Provider : ConvexHull and Centroid.
     660
     661Note that you should define MY_OGR_Centroid function before the Centroid one as OGR_G_Centroid don't return a geometry object but set the value to an already existing one and support only Polygon as input, so to ensure we use the ConvexHull for MultiPolygon. So please use the code bellow :
     662
     663{{{
     664#!c
     665  OGRGeometryH MY_OGR_G_Centroid(OGRGeometryH hTarget){
     666  OGRGeometryH res;
     667  res=OGR_G_CreateGeometryFromJson("{\"type\": \"Point\", \"coordinates\": [0,0] }");
     668  OGRwkbGeometryType gtype=OGR_G_GetGeometryType(hTarget);
     669  if(gtype!=wkbPolygon){
     670    hTarget=OGR_G_ConvexHull(hTarget);
     671  }
     672  OGR_G_Centroid(hTarget,res);
     673  return res;
     674}
     675}}}
     676
     677To deploy your Services, you only have to copy the Boundary.zcfg metadata file from your cgi-env directory as ConvexHull.zcfg and Centroid.zcfg. Then, you must rename the Service name on the first line to be able to run and test the Execute request in the same way you did before. You only have to set the Identifier value to ConvexHull or Centroid in your request depending on the Service you want to run.
     678
     679Note here that the GetCapabilities and DescribeProcess requests will return odd results as we didn't modified any metadata informations, you can edit the .zcfg files to set correct values. By the way it can be used for testing purpose, as the input and output get the same name and default/supported formats.
     680
     681=== Python Version ===
     682
     683{{{
     684#!python
     685def ConvexHull(conf,inputs,outputs):
     686    if inputs["InputPolygon"]["mimeType"]=="application/json":
     687        geometry=osgeo.ogr.CreateGeometryFromJson(inputs["InputPolygon"]["value"])
     688    else:
     689        geometry=createGeometryFromWFS(inputs["InputPolygon"]["value"])
     690    rgeom=geometry.ConvexHull()
     691    if outputs["Result"]["mimeType"]=="application/json":
     692        outputs["Result"]["value"]=rgeom.ExportToJson()
     693        outputs["Result"]["mimeType"]="text/plain"
     694    else:
     695        outputs["Result"]["value"]=rgeom.ExportToGML()
     696    geometry.Destroy()
     697    rgeom.Destroy()
     698    return 3
     699}}}
     700
     701Once again, you can easily copy and paste the function for Boundary and simply modify the line where the Geometry method was called. Nevertheless, as we did for the C language we will give you a simple way to get things more generic.
     702
     703First of all, the first step which consists in extracting the InputPolygon Geometry as it will be used in the same way in each Service functions, so we will first create a function which will do that for us. The same thing can also be done for filling the output value, so we will define another function to do that automaticaly. Here is the code of this two functions (extractInputs and outputResult) :
     704
     705{{{
     706#!python
     707def extractInputs(obj):
     708    if obj["mimeType"]=="application/json":
     709        return osgeo.ogr.CreateGeometryFromJson(obj["value"])
     710    else:
     711        return createGeometryFromWFS(obj["value"])
     712    return null
     713
     714def outputResult(obj,geom):
     715    if obj["mimeType"]=="application/json":
     716        obj["value"]=geom.ExportToJson()
     717        obj["mimeType"]="text/plain"
     718    else:
     719        obj["value"]=geom.ExportToGML()
     720}}}
     721
     722We can so minimize the code of the Boundary function to make it simplier using the following function definition :
     723
     724{{{
     725#!python
     726def Boundary(conf,inputs,outputs):
     727    geometry=extractInputs(inputs["InputPolygon"])
     728    rgeom=geometry.GetBoundary()
     729    outputResult(outputs["Result"],rgeom)
     730    geometry.Destroy()
     731    rgeom.Destroy()
     732    return 3
     733}}}
     734
     735Then definition of the ConvexHull and Centroid Services can be achieved using the following code :
     736
     737{{{
     738#!python
     739def ConvexHull(conf,inputs,outputs):
     740    geometry=extractInputs(inputs["InputPolygon"])
     741    rgeom=geometry.ConvexHull()
     742    outputResult(outputs["Result"],rgeom)
     743    geometry.Destroy()
     744    rgeom.Destroy()
     745    return 3
     746
     747def Centroid(conf,inputs,outputs):
     748    geometry=extractInputs(inputs["InputPolygon"])
     749    if geometry.GetGeometryType()!=3:
     750        geometry=geometry.ConvexHull()
     751    rgeom=geometry.Centroid()
     752    outputResult(outputs["Result"],rgeom)
     753    geometry.Destroy()
     754    rgeom.Destroy()
     755    return 3
     756}}}
     757
     758Note, that in Python you also need to use ConvexHull to deal with MultiPolygons.
     759
     760You must now copy the Boundary.zcfg file as we explained for the C version in ConvexHull.zcfg and Centroid.zcfg respectively and then, use make install command to re-deploy and test your Services Provider.
     761
     762== Create the Buffer Service ==
     763
     764We can now work on the Buffer Service, which takes more arguments than the other ones. Indeed, the code is a bit different from the one used to implement the Boundary, ConvexHull and Centroid Services.
     765
     766The Buffer service also takes an input geometry, but uses a BufferDistance parameter. It will also allow you to define LitteralData block as the BufferDistance will be simple integer value. The read access to such kind of input value is made using the same function as used before.
     767
     768=== C Version ===
     769
     770If you go back to the first Boundary Service source code, you should not find the following very complicated. Indeed, you simply have to add the access of the BufferDistance argument and modify the line whenthe OGR_G_Buffer must be called (instead of OGR_G_GetBoundary). Here is the ful lcode :
     771
     772{{{
     773int Buffer(maps*& conf,maps*& inputs,maps*& outputs){
     774  OGRGeometryH geometry,res;
     775  map* tmp1=getMapFromMaps(inputs,"InputPolygon","value");
     776  if(tmp==NULL)
     777    return SERVICE_FAILED;
     778  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     779  if(strncmp(tmp->value,"application/json",16)==0)
     780    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     781  else
     782    geometry=createGeometryFromWFS(conf,tmp->value);
     783  int bufferDistance=1;
     784  tmp=getMapFromMaps(inputs,"BufferDistance","value");
     785  if(tmp!=NULL)
     786    bufferDistance=atoi(tmp->value);
     787  res=OGR_G_Buffer(geometry,bufferDistance,30);
     788  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     789  if(strncmp(tmp1->value,"application/json",16)==0){
     790    addToMap(outputs->content,"value",OGR_G_ExportToJson(res));
     791    addToMap(outputs->content,"mimeType","text/plain");
     792  }
     793  else{
     794    addToMap(outputs->content,"value",OGR_G_ExportToGML(res));
     795  }
     796  outputs->next=NULL;
     797  OGR_G_DestroyGeometry(geometry);
     798  OGR_G_DestroyGeometry(res);
     799  return SERVICE_SUCCEEDED;
     800}
     801}}}
     802
     803The new code must be inserted in your service.c file and need to be recompiled and replace the older version of your ZOO Service Provider in the /usr/lib/cgi-bin/ directory. You must of course place the corresponding ZOO Metadata File in the same directory.
     804
     805As we explained before, ZOO Kernel is permissive in the sense that you can pass more arguments than defined in you zcfg file, so let's try using a copy of the Boundary.zcfg file renamed as Buffer.zcfg and containing the Buffer identifier. Then, please test your service using an Execute request as you did before. You will obtain the buffer result in a ResponseDocument.
     806
     807You may have noted that the above code check if a BufferDistance input was passed to the service. If not, we will use 1 as the default value, which explains why you do not have to use one more input to your previous queries.
     808
     809You can change the BufferDistance value used by your Service to compute Buffer of your geometry by adding it to the DataInputs value in your request. Note that using KVP syntaxe, each DataInputs are separated by a semicolon.
     810
     811So, the previous request :
     812
     813{{{
     814DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%3A8082%2Fgeoserver%2Fows%3FSERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dtopp %3Astates%26SRS%3DEPSG%3A4326%26FeatureID%3Dstates.15
     815}}}
     816
     817Can now be rewritten this way :
     818
     819{{{
     820DataInputs=InputPolygon=Reference@xlink:href=http%3A%2F%2Flocalhost%3A8082%2Fgeoserver%2Fows%3FSERVICE%3DWFS%26REQUEST%3DGetFeature%26VERSION%3D1.0.0%26typename%3Dtopp %3Astates%26SRS%3DEPSG%3A4326%26FeatureID%3Dstates.15;BufferDistance=2
     821}}}
     822
     823Setting BufferDistance value to 2 would give you a different result, then don't pass any other parameter as we defined 1 as the default value in the source code.
     824
     825Here you can find the same query in XML format to use from the http://localhost/test_services.html HTML form :
     826
     827{{{
     828#!xml
     829<wps:Execute service="WPS" version="1.0.0" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 ../wpsExecute_request.xsda">
     830 <ows:Identifier>Buffer</ows:Identifier>
     831 <wps:DataInputs>
     832  <wps:Input>
     833   <ows:Identifier>InputPolygon</ows:Identifier>
     834   <ows:Title>Playground area</ows:Title>
     835   <wps:Reference xlink:href="http://localhost:8082/geoserver/ows/?SERVICE=WFS&amp;REQUEST=GetFeature&amp;VERSION=1.0.0&amp;typename=topp:states&amp;SRS=EPS
     836G:4326&amp;FeatureID=states.15"/>
     837  </wps:Input>
     838  <wps:Input>
     839   <ows:Identifier>BufferDistance</ows:Identifier>
     840   <wps:Data>
     841    <wps:LiteralData uom="degree">2</wps:LiteralData>
     842   </wps:Data>
     843  </wps:Input>
     844 </wps:DataInputs>
     845 <wps:ResponseForm>
     846  <wps:ResponseDocument>
     847   <wps:Output>
     848    <ows:Identifier>Buffer</ows:Identifier>
     849    <ows:Title>Area serviced by playground.</ows:Title>
     850    <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract>
     851   </wps:Output>
     852  </wps:ResponseDocument>
     853 </wps:ResponseForm>
     854</wps:Execute>
     855}}}
     856
     857=== Python Version ===
     858
     859As we already defined the utility functions createGeometryFromWFS and outputResult, the code is as simple as this :
     860
     861{{{
     862#!python
     863def Buffer(conf,inputs,outputs):
     864    geometry=extractInputs(inputs["InputPolygon"])
     865    try:
     866        bdist=int(inputs["BufferDistance"]["value"])
     867    except:
     868        bdist=10
     869    rgeom=geometry.Buffer(bdist)
     870    outputResult(outputs["Result"],rgeom)
     871    geometry.Destroy()
     872    rgeom.Destroy()
     873    return 3
     874}}}
     875
     876We simply added the use of inputs["BufferDistance"]["value"] as arguments of the Geometry instance Buffer method. Once you get this code added to your ogr_ws_service_provider.py file, simply copy it in the ZOO Kernel directory (or type make install from your ZOO Service Provider root directory). Note that you also need the Buffer.zcfg file detailled in the next section.
     877
     878=== The Buffer MetadataFile file ===
     879
     880You must add BufferDistance to the Service Metadata File to let clients know that this Service supports this parameter. To do this, please copy your orginal Boundary.zcfg file as Buffer.zcfg and add the following lines to the DataInputs block :
     881
     882{{{
     883[BufferDistance]
     884Title = Buffer Distance
     885Abstract = Distance to be used to calculate buffer.
     886minOccurs = 0
     887maxOccurs = 1
     888<LiteralData>
     889DataType = float
     890<Default>
     891uom = degree
     892value = 10
     893</Default>
     894<Supported>
     895uom = meter
     896</Supported>
     897</LiteralData>
     898}}}
     899
     900Note that as minOccurs is set to 0 which means that the input parameter is optional and don't have to be passed. You must know that ZOO Kernel will pass the default value to the Service function for an optional parameter with a default value set.
     901
     902You can get a full copy of the Buffer.zcfg file here :
     903
     904http://zoo-project.org/trac/browser/trunk/zoo-services/ogr/base-vect-ops/cgi-env/Buffer.zcfg
     905
     906You can now ask ZOO Kernel for GetCapabilities, DescribeProcess and Execute for the Buffer Service.
     907

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