Changes between Version 4 and Version 5 of ZooWorkshop/FOSS4GJapan/ja/CreatingOGRBasedWebServices


Ignore:
Timestamp:
Oct 29, 2010, 8:46:37 AM (13 years ago)
Author:
hayashi
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ZooWorkshop/FOSS4GJapan/ja/CreatingOGRBasedWebServices

    v4 v5  
    1414翻訳中・・・
    1515
     16== ジオメトリサービスの実装 ==
     17
     18サービスプロバイダー作成と開発をステップバイステップで学習するため、はじめに Boundary 関数専用の非常に簡単なものの作成に着目しましょう。
     19同様の手段を Buffer, Centroid and !ConvexHull の実装に用いられます。
     20
     21メタデータは既にOKです。それでは、サービスのコード作成に入りましょう。
     22ZOOサービスのコーディングの際注意すべき、もっとも重要なことは、あなたの作成する対応サービスは3つの引数(内部の maps データ型または [http://docs.python.org/tutorial/datastructures.html#dictionaries Python dictionaries])、整数型の実行ステータスを示す返り値({{{SERVICE_FAILED}}} or {{{SERVICE_SUCCEEDED}}})を必要とするということです:
     23
     24
     25 * {{{conf}}} : 主な実行環境設定( main.cfg の記載に対応)
     26 * {{{inputs}}} : 入力に関する リクエスト / 初期値
     27 * {{{outputs}}} : 出力に関する リクエスト / 初期値
     28
     29
    1630=== Boundary ===
    17 翻訳中・・・
    18 
    19 ==== C Version ====
    20 翻訳中・・・
    21 
    22 ==== Python Version ====
    23 翻訳中・・・
    24 
    25 ==== Testing the Service using Execute Request ====
    26 翻訳中・・・
    27 
    28 ===== The simple and unreadable way =====
    29 翻訳中・・・
    30 
    31 ===== Simplification and readability of request =====
    32 翻訳中・・・
    33 
    34 === Creating Services for other functions (!ConvexHull and Centroid) ===
    35 翻訳中・・・
    36 
    37 ==== C Version ====
    38 翻訳中・・・
    39 
    40 ==== Python Version ====
    41 翻訳中・・・
     31
     32==== C バージョン ====
     33
     34先に述べたとおり、ZOO カーネルは、 {{{maps}}}と呼ばれる特別なデータ型の中で、あなたの作成したサービス機能の引数を受け渡します。
     35サービスをC言語で記述するために、あなたはこのread/writeモードのデータ型にどのようにアクセスするかを学ぶ必要があります。
     36{{{maps}}} は、 {{{map}}} と呼ばれる {{{name}}} を格納するリストへのリンクで、 {{{content}}} {{{map}}} と次の {{{map}}} へのポインタ(またはそれ以上 {{{map}}} がない場合は {{{NULL}}} )からなります。ここに、あなたが確認可能な{{{zoo-kernel/service.h}}} ファイル内で定義されたデータ型を示します:
     37
     38{{{
     39#!c
     40typedef struct maps{
     41    char* name;
     42    struct map* content;
     43    struct maps* next;
     44} maps;
     45}}}
     46
     47{{{maps}}} に含まれる {{{map}}} も単純なリンクされたリストで、キー値のペアを格納するために用いられます。
     48したがって、{{{map}}} は {{{name}}} と {{{value}}} そして次の {{{map}}} へのポインタからなります。
     49ここに、あなたが確認可能な{{{zoo-kernel/service.h}}} ファイル内で定義されたデータ型を示します:
     50
     51{{{
     52#!c
     53typedef struct map{
     54    char* name;       /* The key */
     55    char* value;      /* The value */
     56    struct map* next; /* Next couple */
     57} map;
     58}}}
     59
     60
     61部分的あるいは全部を埋めたデータ構造対が ZOO カーネルからあなたのサービスへ受け渡されたとして、この意味は、各々の {{{maps}}} 中の既存の{{{map}}}に関わる以外には、mapsの生成に関わらなくてよいという事です。
     62始めに知っておくべき関数は、 maps 内の map の詳細にアクセスするための {{{getMapFromMaps}}} ({{{zoo-kernel/service.h}}} ファイルに定義) です。
     63この関数は次の3つの引数からなります:
     64
     65 * {{{m}}} :  任意のmapを検索するのに用いる maps ポインタの代表値(※先頭ポインタ)
     66 * {{{name}}} : 検索するmapの名称を示す char* の代表値(※文字列型の先頭ポインタ)
     67 * {{{key}}} : name という名称の map 内の固有キー
     68
     69例えば、{{{inputs}}}という名の{{{maps}}} 内の {{{InputPolygon}}} の値の {{{map}}} にアクセスする構文は次の通りです。Cコードではこのようになります:
     70
     71{{{
     72#!c
     73map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     74}}}
     75
     76map を得た時点で、次のような構文で値フィールドにアクセスすることが可能です:
     77
     78{{{
     79#!c
     80tmp->name
     81tmp->value
     82}}}
     83
     84
     85maps {{{map}}} のフィールドへのアクセスと読み込みはご存じのとおりです、今からこのようなデータ構造への書き込みはどのように行うかを学習しましょう。
     86これもまた {{{zoo-kernel/service.h}}} ファイルに定義された、単純な {{{setMapInMaps}}} 関数を用います。 {{{setMapInMaps}}} 関数は4つの引数を必要とします:
     87
     88 * {{{m}}} : 更新を行う {{{maps}}} ポンタ,
     89 * {{{ns}}} : 更新を行う  {{{maps}}} の名称,
     90 * {{{n}}} : 値の更新を行う {{{map}}} の名称,
     91 * {{{v}}} : この map にセットする値.
     92
     93これは、どのようにして{{{outputs}}}の{{{Result}}} {{{maps}}} 内のいくつかの {{{map}}} の値を追加または編集するかという例です:
     94
     95{{{
     96#!c
     97setMapInMaps(outputs,"Result","value","Hello from the C World !");
     98setMapInMaps(outputs,"Result","mimeType","text/plain");
     99setMapInMaps(outputs,"Result","encoding","UTF-8");
     100}}}
     101
     102既存の {{{map}}} の生成と更新には {{{setMapInMaps}}} 関数が使用されるということに注意してください。確かに、もし « value » と呼ばれる {{{map}}} が既存の場合(※NULLではないという意味)、その値は自動的に更新されます。
     103このワークショップでは、{{{maps}}} の {{{map}}} を使う場合、 {{{zoo-kernel/service.h}}} に定義された {{{addToMap}}} 関数を直接使用し、 {{{map}}} 内の値の追加と更新を行うこともできます:
     104
     105 * {{{m}}} : a {{{map}}} pointer you want to update,
     106 * {{{n}}} : the name of the {{{map}}} you want to add or update the value,
     107 * {{{v}}} : the value you want to set in this {{{map}}}.
     108
     109このデータ型はすべてのC言語ベースのZOOサービスにおいて使用されるほど、本当に重要です。そしてそれは、個々のデータ型の利用を除いて、他の言語でも同様にであることを示しています。たとえば Python の場合、ディクショナリー型が用いられ、さらに簡単に操作することができます。
     110
     111ここに、Python言語による {{{maps}}} データ型の対応例があります(これは {{{maps}}}の main 設定内の要約です):
     112
     113{{{
     114#!python
     115main={
     116  "main": {
     117    "encoding": "utf-8",
     118    "version": "1.0.0",
     119    "serverAddress": "http://www.zoo-project.org/zoo/",
     120    "lang": "fr-FR,en-CA"
     121  },
     122  "identification": {"title": "The Zoo WPS Development Server",
     123    "abstract": "Development version of ZooWPS.",
     124    "fees": "None",
     125    "accessConstraints": "none",
     126    "keywords": "WPS,GIS,buffer"
     127  }
     128}
     129}}}
     130
     131maps と mapにどのように対応するかを知るために、OGR Boundary 関数を使用した最初の ZOO サービスを書く準備をしましょう。
     132
     133すでにはじめにで述べたように、OSGeoLive に準備された Geoserver WFS server を使用します、それで完全なWFS レスポンスを入力値に利用することが出来るでしょう。私たちは、完全なWFSレスポンスというよりはむしろ、ジオメトリオブジェクトだけを簡単に利用する [http://www.gdal.org/ogr/ogr__api_8h.html#a797af4266c02846d52b9cf3207ef958 OGR_G_GetBoundary]のように OGR Geometry 関数を使用しましょう。まずはじめにすることは、完全なWFSレスポンスに定義されたジオメトリを抽出する関数を書くことです。私たちはそれを createGeometryFromWFS と呼びましょう。
     134
     135これはそのようなコードです:
     136
     137{{{
     138#!c
     139OGRGeometryH createGeometryFromWFS(maps* conf,char* inputStr){
     140  xmlInitParser();
     141  xmlDocPtr doc = xmlParseMemory(inputStr,strlen(inputStr));
     142  xmlChar *xmlbuff;
     143  int buffersize;
     144  xmlXPathContextPtr xpathCtx;
     145  xmlXPathObjectPtr xpathObj;
     146  char * xpathExpr="/*/*/*/*/*[local-name()='Polygon' or local-name()='MultiPolygon']";
     147  xpathCtx = xmlXPathNewContext(doc);
     148  xpathObj = xmlXPathEvalExpression(BAD_CAST xpathExpr,xpathCtx);
     149  if(!xpathObj->nodesetval){
     150    errorException(conf, "Unable to parse Input Polygon","InvalidParameterValue");
     151    exit(0);
     152  }
     153  int size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
     154  xmlDocPtr ndoc = xmlNewDoc(BAD_CAST "1.0");
     155  for(int k=size-1;k>=0;k--){
     156    xmlDocSetRootElement(ndoc, xpathObj->nodesetval->nodeTab[k]);
     157  }
     158  xmlDocDumpFormatMemory(ndoc, &xmlbuff, &buffersize, 1);
     159  char *tmp=strdup(strstr((char*)xmlbuff,"?>")+2);
     160  xmlXPathFreeObject(xpathObj);
     161  xmlXPathFreeContext(xpathCtx);
     162  xmlFree(xmlbuff);
     163  xmlFreeDoc(doc);
     164  xmlCleanupParser();
     165  OGRGeometryH res=OGR_G_CreateFromGML(tmp);
     166  if(res==NULL){
     167    errorException(conf, "Unable to call OGR_G_CreatFromGML","NoApplicableCode");
     168    exit(0);
     169  }
     170  else
     171    return res;
     172}
     173}}}
     174
     175
     176関数の本体に使用された errorException の呼び出しに注目してください。この関数は {{{zoo-kernel/service_internal.h}}} に定義されており、{{{zoo-kernel/service_internal.c}}}に記述されています。それは下記のように3つの引数を必要とします:
     177
     178 * the main environment {{{maps}}},
     179 * a {{{char*}}} representing the error message to display,
     180 * a {{{char*}}} representing the error code (as defined in the WPS specification – Table 62).
     181
     182
     183言い換えると、WFSレスポンスがプロパティを解析されなかった場合、問題が起こったことをクライアントに知らせる {{{ExceptionReport}}} 文が返されます。
     184WFSレスポンスからジオメトリオブジェクトを抽出する関数が書かれ、そしていまから Boundary サービスの定義を始めることができます。これが  Boundary サービスの全体コードです:
     185
     186{{{
     187#!c
     188int Boundary(maps*& conf,maps*& inputs,maps*& outputs){
     189  OGRGeometryH geometry,res;
     190  map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     191  if(tmp==NULL){
     192    setMapInMaps(m,"lenv","message","Unable to parse InputPolygon");
     193    return SERVICE_FAILED;
     194  }
     195  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     196  if(strncmp(tmp1->value,"application/json",16)==0)
     197    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     198  else
     199    geometry=createGeometryFromWFS(conf,tmp->value);
     200  if(geometry==NULL){
     201    setMapInMaps(m,"lenv","message","Unable to parse InputPolygon");
     202    return SERVICE_FAILED;
     203  }
     204  res=OGR_G_GetBoundary(geometry);
     205  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     206  if(strncmp(tmp1->value,"application/json",16)==0){
     207    char *tmp=OGR_G_ExportToJson(res);
     208    setMapInMaps(outputs,"Result","value",tmp);
     209    setMapInMaps(outputs,"Result","mimeType","text/plain");
     210    free(tmp);
     211  }
     212  else{
     213    char *tmp=OGR_G_ExportToGML(res);
     214    setMapInMaps(outputs,"Result","value",tmp);
     215    free(tmp);
     216  }
     217  outputs->next=NULL;
     218  OGR_G_DestroyGeometry(geometry);
     219  OGR_G_DestroyGeometry(res);
     220  return SERVICE_SUCCEEDED;
     221}
     222}}}
     223
     224上記のコードに見られるように、私たちのサービスに受け渡されたデータインプットの mimeType が初めにチェックされます:
     225
     226{{{
     227#!c
     228  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     229  if(strncmp(tmp1->value,"application/json",16)==0)
     230    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     231  else
     232    geometry=createGeometryFromWFS(conf,tmp->value);
     233}}}
     234
     235基本的に、 {{{application/json}}} に設定された {{{mimeType}}} を持つ入力を受け取った場合、ローカル関数の{{{OGR_G_CreateGeometryFromJson}}} が、そうでない場合は {{{createGeometryFromWFS}}} を用います。
     236入力値はいつも同様の種類ではないことに注意してください。実際には、 {{{OGR_G_CreateGeometryFromJson}}} を直接使用することは、JSON文字列は完全なGeoJSONのものではなくてジオメトリオブジェクトだけを含むものであることを意味します。とはいえ、あなたはこのコードをGeoJSON 文字列からジオメトリオブジェクトを抽出する関数を作り出すよう、簡単に完全なGeoJSON文字列を使えるように書き換えることができます(OGR GeoJSON Driverにも利用されているjson-cライブラリのインスタンスを使うことにより)。
     237
     238入力されたジオメトリオブジェクトにアクセスできると、[http://www.gdal.org/ogr/ogr__api_8h.html#a797af4266c02846d52b9cf3207ef958 OGR_G_GetBoundary] 関数を用いて、 res のジオメトリオブジェクトに結果を格納することができます。その時、あなたはサポートされた出力フォーマットとして宣言した初期値の GeoJSON または GML として正しいフォーマットの値だけを格納しなければなりません。(※zcfgにそのように記載したので)
     239ZOO カーネルは、事前にフィルされた出力値を提供し、たとえもしわれわれの例が、{{{application/json}}} よりもむしろ {{{text/plain}}} を使用する mimeType を使用して書き換えた場合は、あなたはキー名に対応する値を埋めることだけを行うように注意してください。実際には、クライアントからリクエストされたフォーマットにより(あるいは初期値のもの)、わたしたちは JSON または GML 表現のジオメトリを準備ます。
     240
     241
     242{{{
     243#!c
     244  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     245  if(strncmp(tmp1->value,"application/json",16)==0){
     246    char *tmp=OGR_G_ExportToJson(res);
     247    setMapInMaps(outputs,"Result","value",tmp);
     248    setMapInMaps(outputs,"Result","mimeType","text/plain");
     249    free(tmp);
     250  }
     251  else{
     252    char *tmp=OGR_G_ExportToGML(res);
     253    setMapInMaps(outputs,"Result","value",tmp);
     254    free(tmp);
     255  }
     256}}}
     257
     258Boundary ZOOサービスは実装されたので、あなたは共有ライブラリを作るためそれをコンパイルする必要があります。 service.hに定義された関数 ({{{getMapFromMaps}}}, {{{setMapInMaps}}} と {{{addToMap}}})を使用したので、あなたはそれらのファイルをあなたのCコードに含める必要があります。同様の必要性は {{{zoo-kernel/service_internal.h}}}に宣言された errorException 関数にもあります。また、あなたのサービスオブジェクトファイルをランタイムの際の {{{errorException}}} に使用される {{{zoo-kernel/service_internal.o}}} をリンクする必要があります。そして、libxml2 と OGR C-API にアクセスするために必要となるファイルもまた含めなければなりません。
     259
     260シェアードライブラリに必要なものとして、 {{{extern "C"}}}を宣言するブロックコード内にあなたのコードを格納しなければなりません。サービスプロバイダディレクトリ( {{{/home/zoows/sources/zoo-services/ws_sp}}}内の)ルートに置く service.c ファイルの中に最終的なコードを格納されなければなりません。それはこのように見えます:
     261
     262{{{
     263#!c
     264#include "ogr_api.h"
     265#include "service.h"
     266extern "C" {
     267#include <libxml/tree.h>
     268#include <libxml/parser.h>
     269#include <libxml/xpath.h>
     270#include <libxml/xpathInternals.h>
     271<YOUR SERVICE CODE AND OTHER UTILITIES FUNCTIONS>
     272}
     273}}}
     274
     275サービスの完全なソースは準備が出来たので、次にシェアードライブラリとしてコードをコンパイルした結果のシェアードオブジェクトに対応するサービスを生成する必要があります。これは次のコマンドを用いて行うことができます:
     276
     277{{{
     278#!c
     279g++ $CFLAGS -shared -fpic -o cgi-env/!ServicesProvider.zo ./service.c $LDFLAGS
     280}}}
     281
     282{{{CFLAGS}}} and {{{LDFLAGS}}} 環境変数は事前にセットする必要があることに注意してください。
     283
     284{{{CFLAGS}}} はインクルードされたヘッダを探すために求められるパスおよび{{{ogr_api.h}}}, libxml2 directory, {{{service.h}}} と {{{service_internal.h}}} ファイルのディレクトリへのパスのすべてを含む必要があります。OSGeoLive 環境のおかげで、いくつかの添付されたツールを用いてそのような値を取り出すことができます:{{{xml2-config}}} と {{{gdal-config}}} どちらも {{{--cflags}}} 条件に用いられます。それらは要求通りのパスを生成します。
     285
     286もし {{{zoo-services}}} 内のメインディレクトリに ZOO サービスのプロバイダーを作成する説明を続けるならば、あなたのカレントパス({{{/home/user/zoows/sources/zoo-services/ws_sp}}})に対して相対的な {{{../../zoo-kernel}}}ディレクトリにある ZOO カーネルとソースツリーを見ることができます。あなたのソースツリーを別の場所に移動させる場合でも全く同様のコマンドラインを使ったコードコンパイルを保持するために相対パスなのですが、ZOO カーネルのフルパスを使用することもできることに注意してください。コンパイラーが {{{service.h}}} と {{{service_internal.h}}} を見つけることが出来るようにするために {{{CFLAGS}}} に {{{-I../../zoo-kernel}}}を加える必要があります。
     287
     288完全な {{{CFLAGS}}} 記述はこの通りです:
     289
     290{{{
     291#!sh
     292CFLAGS=`gdal-config --cflags` `xml2-config --clfags` -I../../zoo-kernel/
     293}}}
     294
     295{{{CFLAGS}}} に正しくセットするインクルードパスをたので、リンクに対応するライブラリ({{{LDFLAGS}}}環境変数として定義)に取り組みましょう。gdal と libxml2 ライブラリに対するリンクの場合、 上記と同じツールに {{{--cflags}}} のかわりに {{{--libs}}} を用いることができます。完全な{{{LDFLAGS}}} の記述はこの通りです:
     296
     297{{{
     298#!sh
     299LDFLAGS=`gdal-config --libs` `xml2-config --libs` ../../zoo-kernel/service_internal.o
     300}}}
     301
     302次にコードのコンパイル時に手助けをする Makefile を作成します。ZOO サービスプロバイダディレクトリのルートに短い Makefile を書きましょう、下記の行を含みます:
     303
     304{{{
     305#!sh
     306ZOO_SRC_ROOT=../../zoo-kernel/
     307CFLAGS=-I${ZOO_SRC_ROOT} `xml2-config --cflags` `gdal-config --cflags`
     308LDFLAGS=`xml2-config --libs` `gdal-config --libs`${ZOO_SRC_ROOT}/service_internal.o
     309
     310cgi-env/ogr_ws_service_provider.zo: service.c
     311    g++ ${CFLAGS} -shared -fpic -o cgi-env/ogr_ws_service_provider.zo ./service.c $ {LDFLAGS}
     312clean:
     313    rm -f cgi-env/ogr_ws_service_provider.zo
     314}}}
     315
     316この {{{Makefile}}} を使用して、ZOO サービスプロバイダディレクトリから make を実行すると、 {{{cgi-env}}} に結果として {{{ogr_ws_service_provider.zo}}} というファイルが得られます。
     317
     318メタデータファイルと ZOO サービスシェアードオブジェクトの両方が {{{cgi-env}}} ディレクトリにできました。新しい !ServicesProvider を配置するために、ZOO カーネルがあるディレクトリ {{{/usr/lib/cgi-bin}}} に ZOOサービスシェアードオブジェクトと対応するメタファイルの両方をコピーしましょう。このタスクを実行するためには {{{sudo}}} コマンドを用いなければなりません:
     319
     320{{{
     321#!sh
     322sudo cp ./cgi-env/* /usr/lib/cgi-bin
     323}}}
     324
     325これで、ZOO サービスのソースツリーの意味するところが解りましたね。{{{cgi-env}}} ディレクトリはあなたの新しいサービスまたはサービスプロバイダを、{{{cgi-env}}} のコンテンツを {{{cgi-bin}}} ディレクトリにコピーするという簡単な方法で 配置をするためのものです。
     326
     327make install と直接タイプすることで、新しいサービスプロバイダを ZOOカーネルで利用可能にするために、 {{{Makefile}}} に下記の行を追加することが出来ることに注意してください:
     328
     329{{{
     330#!sh
     331install:
     332    sudo cp ./cgi-env/* /usr/lib/cgi-bin
     333}}}
     334
     335これで、 ZOO サービスプロバイダは、ZOOカーネルを通しての実行リクエストから利用される準備が出来ました。
     336
     337==== Python バージョン ====
     338
     339ZOO のサービスするプロバイダの実装に Python を使用するため、cgi-env ディレクトリにある ogr_ws_service_provider.py のすべてのコピーするコードは下記のとおりです。実際、Pythonのようなインタプリタ言語では、極めて簡単な配置ステップで作られるサービスの配置以外にはなにもコンパイルする必要がありません:
     340
     341{{{
     342#!python
     343import osgeo.ogr
     344import libxml2
     345
     346def createGeometryFromWFS(my_wfs_response):
     347    doc=libxml2.parseMemory(my_wfs_response,len(my_wfs_response))
     348    ctxt = doc.xpathNewContext()
     349    res=ctxt.xpathEval("/*/*/*/*/*[local-name()='Polygon' or local- name()='MultiPolygon']")
     350    for node in res:
     351        geometry_as_string=node.serialize()
     352        geometry=osgeo.ogr.CreateGeometryFromGML(geometry_as_string)
     353        return geometry
     354    return geometry
     355
     356def Boundary(conf,inputs,outputs):
     357    if inputs["InputPolygon"]["mimeType"]=="application/json":
     358        geometry=osgeo.ogr.CreateGeometryFromJson(inputs["InputPolygon"]["value"])
     359    else:
     360        geometry=createGeometryFromWFS(inputs["InputPolygon"]["value"])
     361    rgeom=geometry.GetBoundary()
     362    if outputs["Result"]["mimeType"]=="application/json":
     363        outputs["Result"]["value"]=rgeom.ExportToJson()
     364        outputs["Result"]["mimeType"]="text/plain"
     365    else:
     366        outputs["Result"]["value"]=rgeom.ExportToGML()
     367    geometry.Destroy()
     368    rgeom.Destroy()
     369    return 3
     370}}}
     371
     372私たちは既に詳細を伝えていて、コードは同様な方法で作られているため、ここでは関数本体については述べません。
     373
     374実行の前にすることは、{{{cgi-env}}} のファイルを {{{cgi-bin}}} にコピーすることだけです:
     375
     376{{{
     377#!sh
     378sudo cp ./cgi-env/* /usr/lib/cgi-bin
     379}}}
     380
     381インストールセクションに対応した簡単な {{{Makefile}}} 次のように書かれます:
     382
     383{{{
     384#!sh
     385install:
     386        sudo cp ./cgi-env/* /usr/lib/cgi-bin/
     387}}}
     388
     389最後に、簡単にZOO サービスプロバイダのメインディレクトリから make install を実行すると、ZOO プロバイダーが配置されます。
     390
     391==== Execute リクエストを使用したテスト ====
     392
     393===== 単純かつ読みづらい方法 =====
     394
     395皆さんは、ご自分の ogr_ws_service_provider と呼ばれる、ZOOカーネルツリーに配置された ZOO サービスプロバイダとして格納された OGR Boundary サービスのコピーがあり、サービスをテストするために Execute リクエストを次の通りに使うことができます:
     396
     397[http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=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 link]
     398
     399{{{
     400http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=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
     401}}}
     402
     403上記のURLに見られるように、 {{{DataInputs}}} KVP 値内の {{{xlink:href}}} キーにある、OSGeoLive上の Geoserver WFSサーバーや、 {{{Reference}}}にセットされる {{{InputPolygon}}} の値を URLエンコードされた WFS リクエストとして使用します。エンコードされていない WFSリクエストに対応したものは次の通りです:
     404
     405[http://localhost:8082/geoserver/ows/?SERVICE=WFS&REQUEST=GetFeature&VERSION=1.0.0&typename=topp:states&SRS=EPSG:4326&FeatureID=states.15]
     406
     407サービスの実行に利用される入力値についての情報を得たい場合、lineage=true を付け加えることができることに注意してください。さらに、後で再利用するために ZOO サービスの {{{ExecuteResponse}}} ドキュメントを格納する必要があるかもしれません。このような場合、前のリクエストに {{{storeExecuteResponse=true}}} を追加してください。このパラメータ指定を true にしていない実行の時と比べて、ZOOカーネルの挙動は正確には同じでないということに注意してください。実際、このようなリクエストでは、ZOO カーネルは statusLocation の属性に対する {{{ExecuteResponse}}} を返し、それは実行中の結果あるいは最後に {{{ExecuteResponse}}} に置かれた情報をクライアントに与えます。
     408
     409リクエスト中で {{{storeExecuteResponse}}} が true に設定された場合、 {{{ExecuteResponse}}} がどのようになるかの例は次の通りです:
     410
     411[[Image(Practical introduction to ZOO - 7.png,width=550px)]]
     412
     413statusLocation にしたがって、以前にリクエストした時に得られたものと同じ {{{ExecuteResponse}}} が得られました。クライアントアプリケーションのキャッシングシステムを備えているということで大変便利になるということに注意してください。
     414
     415以前のリクエストのすべての {{{ResponseForm}}} を指定しておらず、リクエストされなかったなら、作成した zcfg ファイルに定義された初期値で用いられる {{{application/json}}} {{{mimeType}}} に基づく {{{ResponseDocument}}} が返されます。しかしながら、クエリーの {{{ResponseDocument}}} パラメータに {{{mimeType=text/xml}}} 属性を加えることで、ZOO カーネルにどのような種類の結果が欲しいかを伝えることができます。以前のリクエストに対して、このパラメータを追加することで GML 表記の結果を得られます:
     416
     417[http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=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&ResponseDocument=Result@mimeType=text/xml link]
     418
     419{{{
     420http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=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&ResponseDocument=Result@mimeType=text/xml
     421}}}
     422
     423WPS仕様による定義では、完全な {{{ResponseDocument}}} ではなくデータだけの {{{RawDataOutput}}}を問い合わせることができます。そうするためには、次に示されるリクエストのように、リクエストの {{{ResponseDocument}}} を {{{RawDataOutput}}} に置き換えるだけです。:
     424
     425[http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=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&RawDataOutput=Result@mimeType=application/json link]
     426
     427{{{
     428http://localhost/cgi-bin/zoo_loader.cgi?request=Execute&service=WPS&version=1.0.0&Identifier=Boundary&DataInputs=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&RawDataOutput=Result@mimeType=application/json
     429}}}
     430
     431最後に、このワークショップの次のセクションにあるクライアントアプリケーションの開発に使用するために、にこの種のリクエストをJSON文字列形式で取得するよう {{{mimeType}}} の初期値を戻しておくことに注意してください。(※次のセクションでは mimeType=application/json を使用します)
     432
     433===== リクエストの単純化と可読性 =====
     434
     435このワークショップのはじめから用いられた例をみると、複雑で長いURLを作成して GET に用いられるExecute リクエストを書くことは時により困難です。
     436次のリクエスト例では、そういうことですので、POST XML リクエストを用いましょう。初めに、以前の Execute で使用したリクエストに対応する XML は次のとおりです:
     437
     438{{{
     439#!xml
     440<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.xsd">
     441 <ows:Identifier>Boundary</ows:Identifier>
     442 <wps:DataInputs>
     443  <wps:Input>
     444   <ows:Identifier>InputPolygon</ows:Identifier>
     445   <ows:Title>Playground area</ows:Title>
     446   <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=EPSG:43 26&amp;FeatureID=states.15"/>
     447  </wps:Input>
     448 </wps:DataInputs>
     449 <wps:ResponseForm>
     450  <wps:ResponseDocument>
     451   <wps:Output>
     452    <ows:Identifier>Result</ows:Identifier>
     453    <ows:Title>Area serviced by playground.</ows:Title>
     454    <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract>
     455   </wps:Output>
     456  </wps:ResponseDocument>
     457 </wps:ResponseForm>
     458</wps:Execute>
     459}}}
     460
     461XML リクエストを簡単に実行するために、test_services.html と呼ばれる HTML フォームを {{{/var/www}}} に準備しました。次に示すリンクを使用してそれにアクセスすることができます: http://localhost/test_services.html.
     462
     463ブラウザでこのページを開いてください、テキストエリアフィールド内に簡単に XML リクエストを埋めて、« run using XML Request » 送信ボタンをクリックします。サービスが GET リクエストを使用して実行したときと同様に結果を得られます。スクリーンショットは、上記に示されたリクエストを含む HTML フォームとページの下部にある iframe に表示された {{{ExecuteResponse}}}を示します:
     464
     465[[Image(Practical introduction to ZOO - 8.png,width=550px)]]
     466
     467{{{xlink:href}}} の値はこのようなデータ入力を取り扱うもっとも簡単な方法です。もちろん、ジオメトリの完全なJSON文字列を使う事もでき、それは次に示す XML リクエスト例のとおりです:
     468
     469{{{
     470#!xml
     471<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">
     472 <ows:Identifier>Boundary</ows:Identifier>
     473 <wps:DataInputs>
     474  <wps:Input>
     475   <ows:Identifier>InputPolygon</ows:Identifier>
     476   <wps:Data>
     477    <wps:ComplexData mimeType="application/json">
     478{ "type": "MultiPolygon", "coordinates": [ [ [ [ -105.998360, 31.393818 ], [ -106.212753, 31.478128 ], [ -106.383041, 31.733763 ], [ -106.538971, 31.786198 ], [ -106.614441, 31.817728 ], [ -105.769730, 31.170780 ], [ -105.998360, 31.393818 ] ] ], [ [ [ -94.913429, 29.257572 ], [ -94.767380, 29.342451 ], [ -94.748405, 29.319490 ], [ -95.105415, 29.096958 ], [ -94.913429, 29.257572 ] ] ] ] }
     479    </wps:ComplexData>
     480   </wps:Data>
     481  </wps:Input>
     482 </wps:DataInputs>
     483 <wps:ResponseForm>
     484  <wps:ResponseDocument>
     485   <wps:Output>
     486    <ows:Identifier>Result</ows:Identifier>
     487    <ows:Title>Area serviced by playground.</ows:Title>
     488    <ows:Abstract>Area within which most users of this playground will live.</ows:Abstract>
     489   </wps:Output>
     490  </wps:ResponseDocument>
     491 </wps:ResponseForm>
     492</wps:Execute>
     493}}}
     494
     495もし、すべてがうまくいったなら、引数として JSONジオメトリの Boundary が受け渡され、サービスは GML と JSON の両方を入力データとして取り扱えるということです。先のリクエストで、私たちは、{{{ComplexData}}}ノードの属性に追加した{{{mimeType}}}に、 入力データが {{{text/xml}}} {{{mimeType}}} ではなくて {{{application/json}}} 文字列を指定して受け渡される指定をしました。それは、以前に述べたように {{{@mimeType=application/json}}} を追加したのと同じことです。
     496
     497=== その他の関数サービスの作成 (!ConvexHull and Centroid) ===
     498
     499Boudary の単純なサービスコードがありますので、全く同じ数の引数:ジオメトリをもつ !ConvexHull と Centroid を簡単に追加することができます。実装の詳細と !ConvexHull サービスの配置については、下記に示すたとおりです。 Centroid についても同様です。
     500
     501==== C バージョン ====
     502
     503まず次に示すコードを {{{service.c}}} ソースコードに追加しましょう:
     504
     505{{{
     506#!c
     507int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){
     508  OGRGeometryH geometry,res;
     509  map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     510  if(tmp==NULL){
     511    setMapInMaps(conf,"lenv","message","Unable to fetch InputPolygon value.");
     512    return SERVICE_FAILED;
     513  }
     514  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     515  if(strncmp(tmp1->value,"application/json",16)==0)
     516    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     517  else
     518    geometry=createGeometryFromWFS(conf,tmp->value);
     519  if(geometry==NULL){
     520    setMapInMaps(conf,"lenv","message","Unable to parse InputPolygon value.");
     521    return SERVICE_FAILED;
     522  }
     523  res=OGR_G_ConvexHull(geometry);
     524  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     525  if(strncmp(tmp1->value,"application/json",16)==0){
     526    char* tmp=OGR_G_ExportToJson(res);
     527    setMapInMaps(outputs,"Result","value",tmp);
     528    setMapInMaps(outputs,"Result","mimeType","text/plain");
     529    free(tmp);
     530  }
     531  else{
     532    char* tmp=OGR_G_ExportToGML(res);
     533    setMapInMaps(outputs,"Result","value",tmp);
     534    free(tmp);
     535  }
     536  OGR_G_DestroyGeometry(geometry);
     537  OGR_G_DestroyGeometry(res);
     538  return SERVICE_SUCCEEDED;
     539}
     540}}}
     541
     542この新しいコードは、Boundary サービスと全く同じです。ただ一つ我々が変更するのは、[http://www.gdal.org/ogr/ogr__api_8h.html#7a93026cfae8ee6ce25546dba1b2df7d OGR_G_ConvexHull] 関数が呼ばれている行の部分です (前に使用した {{{OGR_G_GetBoundary}}}ではなく)。関数をまったくコピーペーストすることは良くありませんので、すべての場合において、新しいサービスの関数本体の記述を行うもっと一般的な方法があります。次に示す常用関数は単純化のためのものです:
     543
     544{{{
     545#!c
     546int applyOne(maps*& conf,maps*& inputs,maps*& outputs,OGRGeometryH (*myFunc) (OGRGeometryH)){
     547  OGRGeometryH geometry,res;
     548  map* tmp=getMapFromMaps(inputs,"InputPolygon","value");
     549  if(tmp==NULL){
     550    setMapInMaps(conf,"lenv","message","Unable to fetch InputPolygon value.");
     551    return SERVICE_FAILED;
     552  }
     553  map* tmp1=getMapFromMaps(inputs,"InputPolygon","mimeType");
     554  if(strncmp(tmp1->value,"application/json",16)==0)
     555    geometry=OGR_G_CreateGeometryFromJson(tmp->value);
     556  else
     557    geometry=createGeometryFromWFS(conf,tmp->value);
     558  if(geometry==NULL){
     559    setMapInMaps(conf,"lenv","message","Unable to parse InputPolygon value.");
     560    return SERVICE_FAILED;
     561  }
     562  res=(*myFunc)(geometry);
     563  tmp1=getMapFromMaps(outputs,"Result","mimeType");
     564  if(strncmp(tmp1->value,"application/json",16)==0){
     565    char *tmp=OGR_G_ExportToJson(res);
     566    setMapInMaps(outputs,"Result","value",tmp);
     567    setMapInMaps(outputs,"Result","mimeType","text/plain");
     568    free(tmp);
     569  }
     570  else{
     571    char *tmp=OGR_G_ExportToGML(res);
     572    setMapInMaps(outputs,"Result","value",tmp);
     573    free(tmp);
     574  }
     575  outputs->next=NULL;
     576  OGR_G_DestroyGeometry(geometry);
     577  OGR_G_DestroyGeometry(res);
     578  return SERVICE_SUCCEEDED;
     579}
     580}}}
     581
     582次に、完全な関数名の代わりに {{{myFunc}}} と呼ばれる関数ポインタを使用します。Boundary サービスをこの方法で再実装する方法は次のとおりです:
     583
     584{{{
     585#!c
     586int Boundary(maps*& conf,maps*& inputs,maps*& outputs){
     587  return applyOne(conf,inputs,outputs,&OGR_G_GetBoundary);
     588}
     589}}}
     590
     591{{{service.c}}} 内に定義された、この {{{applyOne}}} ローカル関数を用いて, その他のサービスを次のように定義できます:
     592
     593{{{
     594#!c
     595int ConvexHull(maps*& conf,maps*& inputs,maps*& outputs){
     596  return applyOne(conf,inputs,outputs,&OGR_G_ConvexHull);
     597}
     598int Centroid(maps*& conf,maps*& inputs,maps*& outputs){
     599  return applyOne(conf,inputs,outputs,&MY_OGR_G_Centroid);
     600}
     601}}}
     602
     603{{{applyOne}}} 関数の一般化により2つの新しいサービス : !ConvexHull and Centroidが ZOO プロバイダに追加されます。
     604
     605Centroid の前に、[http://www.gdal.org/ogr/ogr__api_8h.html#23f5a19a81628af7f9cc59a37378cb2b OGR_G_Centroid]にあるように、返されるジオメトリオブジェクト以外にも既存のポリゴンをセットするジオメトリを入力値とする {{{MY_OGR_Centroid}}} 関数を定義する必要があります。マルチポリゴンの !ConvexHull を確保するためでもあります。コードは次のとおりです:
     606
     607{{{
     608#!c
     609OGRGeometryH MY_OGR_G_Centroid(OGRGeometryH hTarget){
     610  OGRGeometryH res;
     611  res=OGR_G_CreateGeometryFromJson("{\"type\": \"Point\", \"coordinates\": [0,0] }");
     612  OGRwkbGeometryType gtype=OGR_G_GetGeometryType(hTarget);
     613  if(gtype!=wkbPolygon){
     614    hTarget=OGR_G_ConvexHull(hTarget);
     615  }
     616  OGR_G_Centroid(hTarget,res);
     617  return res;
     618}
     619}}}
     620
     621サービスを配置するため、 cgi-env ディレクトリから {{{ConvexHull.zcfg}}} と {{{Centroid.zcfg}}} として、 {{{Boundary.zcfg}}} をコピーします。
     622次に、前にも行ったとおりの手順で、 {{{Execute}}} リクエストの実行とテストのために、最初の行のサービス名を書き換える必要があります。Idenitifier の値を、実行するサービスのリクエストに対応するように、 !ConvexHull または Centroid に書き換えます。
     623
     624ここでは、 {{{GetCapabilities}}} と {{{DescribeProcess}}} リクエストはメタデータを書き換えなかったので、中途半端な(Boundaryの)情報を返すこと、.zcfg に修正した値をセットできることに注意してください。ところで、テストの目的で使用されるので、入力と出力の名前と、default/supported フォーマットも同じものが使われています。
     625
     626==== Python バージョン ====
     627
     628{{{
     629#!python
     630def ConvexHull(conf,inputs,outputs):
     631    if inputs["InputPolygon"]["mimeType"]=="application/json":
     632        geometry=osgeo.ogr.CreateGeometryFromJson(inputs["InputPolygon"]["value"])
     633    else:
     634        geometry=createGeometryFromWFS(inputs["InputPolygon"]["value"])
     635    rgeom=geometry.ConvexHull()
     636    if outputs["Result"]["mimeType"]=="application/json":
     637        outputs["Result"]["value"]=rgeom.ExportToJson()
     638        outputs["Result"]["mimeType"]="text/plain"
     639    else:
     640        outputs["Result"]["value"]=rgeom.ExportToGML()
     641    geometry.Destroy()
     642    rgeom.Destroy()
     643    return 3
     644}}}
     645
     646再度、Boundary の関数を簡単にコピーペーストして、Geometry関数が呼ばれている行を変更します。やはり、C言語で行ったように、もっと一般化して単純にする方法を教えましょう。
     647
     648まずはじめに、各々のサービス関数で同様に用いられる InputPolygon ジオメトリの抽出にある最初のステップですが、まずはじめにその関数を作りましょう。
     649同様に出力値を埋め、他の関数に対しても自動的に行われるよう定義しましょう。2つの関数 (extractInputs and outputResult) はつぎのとおりです:
     650
     651{{{
     652#!python
     653def extractInputs(obj):
     654    if obj["mimeType"]=="application/json":
     655        return osgeo.ogr.CreateGeometryFromJson(obj["value"])
     656    else:
     657        return createGeometryFromWFS(obj["value"])
     658    return null
     659
     660def outputResult(obj,geom):
     661    if obj["mimeType"]=="application/json":
     662        obj["value"]=geom.ExportToJson()
     663        obj["mimeType"]="text/plain"
     664    else:
     665        obj["value"]=geom.ExportToGML()
     666}}}
     667
     668Boundary 関数のコードを、下記に示す関数定義により、最小にすることができます:
     669
     670{{{
     671#!python
     672def Boundary(conf,inputs,outputs):
     673    geometry=extractInputs(inputs["InputPolygon"])
     674    rgeom=geometry.GetBoundary()
     675    outputResult(outputs["Result"],rgeom)
     676    geometry.Destroy()
     677    rgeom.Destroy()
     678    return 3
     679}}}
     680
     681次に、!ConvexHull and Centroid サービスの定義については下記コードになります:
     682
     683{{{
     684#!python
     685def ConvexHull(conf,inputs,outputs):
     686    geometry=extractInputs(inputs["InputPolygon"])
     687    rgeom=geometry.ConvexHull()
     688    outputResult(outputs["Result"],rgeom)
     689    geometry.Destroy()
     690    rgeom.Destroy()
     691    return 3
     692
     693def Centroid(conf,inputs,outputs):
     694    geometry=extractInputs(inputs["InputPolygon"])
     695    if geometry.GetGeometryType()!=3:
     696        geometry=geometry.ConvexHull()
     697    rgeom=geometry.Centroid()
     698    outputResult(outputs["Result"],rgeom)
     699    geometry.Destroy()
     700    rgeom.Destroy()
     701    return 3
     702}}}
     703
     704Pythonにおいても。マルチポリゴンを取り扱う !ConvexHull を使う必要があるということに注意してください。
     705
     706Cバージョンで説明したように、Boundary.zcfg を {{{ConvexHull.zcfg}}} と {{{Centroid.zcfg}}} の各々にコピーしてください。
     707そして、make install コマンドを使い、サービスプロバイダを再配置します。
     708
    42709
    43710=== バッファサービス作成 ===

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