QGISサーバーPythonのプラグイン

PythonのプラグインもQGIS Server上で実行することができます(参照:は:ref: label_qgisserver):(:クラス:` QgsServerInterface`)*サーバインタフェース*を使用してサーバー上で実行されているPythonのプラグインは、既存のコアサービスの動作を変更することができます( ** WMS WFS **など)。

(:クラス: QgsServerFilter)*サーバフィルタインタフェース*で、私たちは、生成された出力を変更したりしても、新たなサービスを提供することで、入力パラメータを変更することができます。

(:クラス: QgsAccessControlFilter)*アクセス制御インタフェース*で、リクエストごとにいくつかのアクセス制限を適用できます。

サーバーフィルタープラグインアーキテクチャ

FCGIアプリケーションの起動時にサーバーのpythonプラグインは、一度ロードされます。クラス::彼らは、1つまたは複数の登録`QgsServerFilter`を(この時点から、あなたは`サーバーのプラグインAPIドキュメントに役立つ簡単に見て見つけるかもしれない<http://qgis.org/api/group__server.html> `_)。各フィルタは、少なくとも3つのコールバックを実装する必要があります。

  • requestReady()
  • responseComplete()
  • sendResponse()

(:クラス: QgsRequestHandler)すべてのフィルタは、リクエスト/レスポンスオブジェクトへのアクセス権を持っており、そのすべてのプロパティ(入力/出力)を操作し、(以下で見るように非常に特定の方法で)例外を発生させることができます。

ここでは、フィルタのコールバックが呼ばれ、一般的なサーバーのセッションとを示す擬似コードは次のとおりです。

  • 着信要求を取得
    • GET / POST / SOAPリクエストハンドラを作成

    • クラス::のインスタンスにリクエストを渡す QgsServerInterface

    • プラグインを呼び出す:FUNC: `requestReady`フィルター

    • 応答がない場合
      • サービスは、WMS / WFS / WCSであれば
        • WMS / WFS / WCSサーバを作成
          • FUNC:サーバーの呼び出し executeRequest`とpossibily呼び出す:FUNC: sendResponse`プラグインフィルタを出力をストリーミングするとき、または要求ハンドラのバイトストリーム出力およびコンテンツタイプを保存します

      • プラグインを呼び出す:FUNC: `responseComplete`フィルター

    • :func: `sendResponse`フィルタプラグインを呼び出します

    • 要求ハンドラの出力応答

次の段落では、詳細で利用可能なコールバックを記述する。

requestReady

要求の準備ができたときに呼び出されます。受信URLとデータが解析され、コアサービス(WMS、WFSなど)スイッチに入る前に、これはあなたが入力を操作するなどのアクションを実行することができますポイントです。

  • 認証/認可

  • リダイレクト

  • 追加/特定のパラメータ(例えば、型名)を除去

  • 例外を発生させます

あなたも** SERVICE **パラメータを変更するので、完全にコアサービスをバイパスすることによって、完全にコアサービスを置き換えることができ(とはいえ、これはあまり意味がないということ)。

sendResponse

出力が送られるたびに呼び出され** FCGI ** stdout`コアサービスは、そのプロセスを終了しresponseCompleteフックが呼ばれた後、しかし、いくつかの例をXMLにした後、これが正常に行われている(そしてそこから、クライアントへ)ストリーミングXMLの実装が必要とされたことをとても巨大になることができ、この場合、(WFS GetFeatureのは、そのうちの一つである):funcが: `sendResponse`は応答が完了する前に複数回呼び出さ(と前にされた:FUNC: responseComplete`が呼び出されます)。FUNC:明白な結果はつまり sendResponse`は正常に一度呼び出されますが、非常に複数回呼び出されるかもしれないし、その場合には(そして唯一のそのような場合には)それはまたの前に呼び出されます:FUNC: responseComplete`。

:funcが: sendResponse`は、コアサービスの出力を直接操作のために、しばらく最高の場所です:FUNC: responseComplete`は、また、一般的なオプションです:FUNC: `sendResponse`は、ストリーミングサービスの場合の唯一の実行可能な選択肢です。

responseComplete

(ヒットした場合)コアサービスは、そのプロセスを終了するときに一度だけ呼び出され、要求がクライアントに送信する準備ができています。FUNC:FUNC:: sendResponse`呼ばれたかもしれないストリーミングサービス(または他のプラグインフィルター)を除いて上述したように、これは通常の前に呼び出された sendResponse`を早く。

:FUNC: `responseComplete`は、新しいサービスの実装(WPSまたはカスタムサービス)を提供し、コアサービスからの出力を直接操作を実行するために理想的な場所である(例えばWMSの画像に透かしを追加するため)。

プラグインから例外を上げます

いくつかの作品は、まだこのトピックに行う必要があります:現在の実装では、設定によって処理し、未処理の例外を区別することができます:class: QgsRequestHandler`プロパティのインスタンスへ:クラス: QgsMapServiceException`、このようメインC ++のコードをキャッチすることができますPythonの例外を取り扱い、未処理の例外を無視(またはそれ以上を:それらをログに記録)。

このアプローチは、基本的に動作しますが、それは非常に「神託」ではありません:より良いアプローチは、Pythonコードから例外が発生し、それらが処理されるためのC ++ループに湧き上がっ見ることであろう。

サーバー・プラグインを書く

参考文献: developing_plugins、単に追加の(または代替)インタフェースを提供する:に記載されているようにちょうど標準QGIS Pythonのプラグインであるプラグインサーバクラス:` QgisInterface`インスタンス典型的QGISデスクトッププラグインを介してQGISアプリケーションへのアクセスを有していますクラス:: QgsServerInterface、サーバプラグインもへのアクセスをしています。

プラグインは、サーバ・インタフェースを持つQGISサーバーを指示するには、特別なメタデータエントリは(metadata.txt` `に)必要とされている:

server=True

ここでは(より多くの例フィルタ付き)議論プラグインを例はgithubの上で提供されています: `QGISのHelloServer例プラグイン<https://github.com/elpaso/qgis-helloserver> ` _

プラグインファイル

ここに私たちの例のサーバー・プラグインのディレクトリ構造です

PYTHON_PLUGINS_PATH/
  HelloServer/
    __init__.py    --> *required*
    HelloServer.py  --> *required*
    metadata.txt   --> *required*

__init__.py

このファイルは、Pythonのインポートシステムで必要とされます。:func: `serverClassFactoryサーバーの起動時にプラグインがQGIS Serverにロードされるときに呼び出される()`関数、また、QGIS Serverは、このファイルが含まれていることが必要です。クラス:: `QgsServerInterface`とプラグインのクラスのインスタンスを返す必要がありますそれはのインスタンスへの参照を受け取ります。これは例では、 `__init __プラグインをする方法です。py`がどのように見える:

# -*- coding: utf-8 -*-

def serverClassFactory(serverIface):
    from HelloServer import HelloServerServer
    return HelloServerServer(serverIface)

HelloServer.py

魔法が起こると、これは魔法がどのように見えるかであるところである:(例:ファイル: HelloServer.py

サーバー・プラグインは通常、QgsServerFilterと呼ばれるオブジェクトに詰め込ま一回の以上のコールバックで構成されています。

各:クラス: `QgsServerFilter`は、次のコールバックの一つ以上を実装します。

  • requestReady()
  • responseComplete()
  • sendResponse()

次の例では、**サービスが**パラメータが等しい場合には、HelloServer **印刷し、最小限のフィルタを実装する「HELLO」:

from qgis.server import *
from qgis.core import *

class HelloFilter(QgsServerFilter):

    def __init__(self, serverIface):
        super(HelloFilter, self).__init__(serverIface)

    def responseComplete(self):
        request = self.serverInterface().requestHandler()
        params = request.parameterMap()
        if params.get('SERVICE', '').upper() == 'HELLO':
            request.clearHeaders()
            request.setHeader('Content-type', 'text/plain')
            request.clearBody()
            request.appendBody('HelloServer!')

フィルタは、次の例のように** ** serverIfaceに登録する必要があります:

class HelloServerServer:
    def __init__(self, serverIface):
        # Save reference to the QGIS server interface
        self.serverIface = serverIface
        serverIface.registerFilter( HelloFilter, 100 )

FUNC:の第2のパラメータは `registerFilter`は、同じ名前(低い優先順位が最初に呼び出された)とのコールバックの順序を定義する優先順位を設定することを可能にします。

3つのコールバックを使用することにより、プラグインは、入力および/またはさまざまな方法でサーバーの出力を操作することができます。クラス: QgsServerInterface、:クラス:` QgsRequestHandler`がコアに入る前に、入力パラメータを変更するために使用できる方法の多くを有する貫通 QgsRequestHandler:クラス:すべての瞬間に、プラグインのインスタンスは、へのアクセスを有しますサーバの処理(:FUNC:使用して requestReady)または要求がコアサービスによって処理された後(使用して:FUNC:` sendResponse`)。

次の例は、いくつかの一般的なユースケースをカバーします:

入力を変更します

例えば、プラグインは、新しいパラメータが中に注入され、この例では、クエリ文字列からの入力パラメータを変更する試験例を含んでいる(既に解析された) `parameterMap`は、このパラメータはで、コアサービス(WMSなど)によって、次に表示されていますコアサービス処理の終了は、パラメータがまだあることを確認してください:

from qgis.server import *
from qgis.core import *

class ParamsFilter(QgsServerFilter):

    def __init__(self, serverIface):
        super(ParamsFilter, self).__init__(serverIface)

    def requestReady(self):
        request = self.serverInterface().requestHandler()
        params = request.parameterMap( )
        request.setParameter('TEST_NEW_PARAM', 'ParamsFilter')

    def responseComplete(self):
        request = self.serverInterface().requestHandler()
        params = request.parameterMap( )
        if params.get('TEST_NEW_PARAM') == 'ParamsFilter':
            QgsMessageLog.logMessage("SUCCESS - ParamsFilter.responseComplete", 'plugin', QgsMessageLog.INFO)
        else:
            QgsMessageLog.logMessage("FAIL    - ParamsFilter.responseComplete", 'plugin', QgsMessageLog.CRITICAL)

これは、ログファイルに見るものの抽出物である:

src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloServerServer - loading filter ParamsFilter
src/core/qgsmessagelog.cpp: 45: (logMessage) [1ms] 2014-12-12T12:39:29 Server[0] Server plugin HelloServer loaded!
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 Server[0] Server python plugins loaded
src/mapserver/qgsgetrequesthandler.cpp: 35: (parseInput) [0ms] query string is: SERVICE=HELLO&request=GetOutput
src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [1ms] inserting pair SERVICE // HELLO into the parameter map
src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [0ms] inserting pair REQUEST // GetOutput into the parameter map
src/mapserver/qgsserverfilter.cpp: 42: (requestReady) [0ms] QgsServerFilter plugin default requestReady called
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloFilter.requestReady
src/mapserver/qgis_map_serv.cpp: 235: (configPath) [0ms] Using default configuration file path: /home/xxx/apps/bin/admin.sld
src/mapserver/qgshttprequesthandler.cpp: 49: (setHttpResponse) [0ms] Checking byte array is ok to set...
src/mapserver/qgshttprequesthandler.cpp: 59: (setHttpResponse) [0ms] Byte array looks good, setting response...
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloFilter.responseComplete
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] SUCCESS - ParamsFilter.responseComplete
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] RemoteConsoleFilter.responseComplete
src/mapserver/qgshttprequesthandler.cpp: 158: (sendResponse) [0ms] Sending HTTP response
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloFilter.sendResponse

13行目の「SUCCESS」の文字列は、プラグインがテストに合格したことを示しています。

同じ手法ではなく、コア1のカスタムサービスを利用するために利用することができます:あなたは、たとえば** WFSをスキップすることができだけに** SERVICE パラメータを変更することで ** ** SERVICE要求または任意の他のコア要求別の何かとコアサービスはスキップされます、あなたは(これはここでは、以下に説明される)出力にカスタム結果を注入し、クライアントに送信できます。

出力の変更または交換

透かしフィルタの例は、WMSコアサービスによって生成されたWMS画像の上に透かし画像を加算した新たな画像とWMS出力を交換する方法を示し:

import os

from qgis.server import *
from qgis.core import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *


class WatermarkFilter(QgsServerFilter):

    def __init__(self, serverIface):
        super(WatermarkFilter, self).__init__(serverIface)

    def responseComplete(self):
        request = self.serverInterface().requestHandler()
        params = request.parameterMap( )
        # Do some checks
        if (request.parameter('SERVICE').upper() == 'WMS' \
                and request.parameter('REQUEST').upper() == 'GETMAP' \
                and not request.exceptionRaised() ):
            QgsMessageLog.logMessage("WatermarkFilter.responseComplete: image ready %s" % request.infoFormat(), 'plugin', QgsMessageLog.INFO)
            # Get the image
            img = QImage()
            img.loadFromData(request.body())
            # Adds the watermark
            watermark = QImage(os.path.join(os.path.dirname(__file__), 'media/watermark.png'))
            p = QPainter(img)
            p.drawImage(QRect( 20, 20, 40, 40), watermark)
            p.end()
            ba = QByteArray()
            buffer = QBuffer(ba)
            buffer.open(QIODevice.WriteOnly)
            img.save(buffer, "PNG")
            # Set the body
            request.clearBody()
            request.appendBody(ba)

この例では**パラメータ値がチェックされ** SERVICE着信要求が** WMS ** GetMapリクエスト**と例外がこの中に(WMS以前に実行プラグインによってまたはコアサービスによって設定されていないである場合ケース)、WMSは、生成された画像は、出力バッファから取得され、透かし画像が追加されます。最後のステップは、出力バッファをクリアして、新たに生成された画像に置き換えることです。実世界の状況で、我々はまた、代わりにどのような場合にPNGを返す要求された画像の種類を確認する必要があることに注意してください。

アクセス制御プラグイン

プラグインファイル

ここに私たちの例のサーバー・プラグインのディレクトリ構造です:

PYTHON_PLUGINS_PATH/
  MyAccessControl/
    __init__.py    --> *required*
    AccessControl.py  --> *required*
    metadata.txt   --> *required*

__init__.py

このファイルは、Pythonのインポートシステムで必要とされます。FUNC:サーバーの起動時にプラグインがQGIS Serverにロードされるときに呼び出される `serverClassFactory()`関数、すべてのQGISサーバプラグインのように、このファイルが含まれています。クラス:: `QgsServerInterface`とプラグインのクラスのインスタンスを返す必要がありますそれはのインスタンスへの参照を受け取ります。これは例では、 `__init __プラグインの方法ですpy`が見えます。: 67

# -*- coding: utf-8 -*-

def serverClassFactory(serverIface):
    from MyAccessControl.AccessControl import AccessControl
    return AccessControl(serverIface)

AccessControl.py

class AccessControl(QgsAccessControlFilter):

    def __init__(self, server_iface):
        super(QgsAccessControlFilter, self).__init__(server_iface)

    def layerFilterExpression(self, layer):
        """ Return an additional expression filter """
        return super(QgsAccessControlFilter, self).layerFilterExpression(layer)

    def layerFilterSubsetString(self, layer):
        """ Return an additional subset string (typically SQL) filter """
        return super(QgsAccessControlFilter, self).layerFilterSubsetString(layer)

    def layerPermissions(self, layer):
        """ Return the layer rights """
        return super(QgsAccessControlFilter, self).layerPermissions(layer)

    def authorizedLayerAttributes(self, layer, attributes):
        """ Return the authorised layer attributes """
        return super(QgsAccessControlFilter, self).authorizedLayerAttributes(layer, attributes)

    def allowToEdit(self, layer, feature):
        """ Are we authorise to modify the following geometry """
        return super(QgsAccessControlFilter, self).allowToEdit(layer, feature)

    def cacheKey(self):
        return super(QgsAccessControlFilter, self).cacheKey()

この例では、皆のための完全なアクセスを提供します。

これは、ログオンしているかを知るためのプラグインの役割です。

これらすべての方法で私達は、層ごとの制限をカスタマイズできるようにするには、引数の層を持っています。

層のフィルタ式

結果を制限するために式を追加するために使用し、例えば:

def layerFilterExpression(self, layer):
    return "$role = 'user'"

属性の役割がある機能に制限するには、「ユーザー」に等しいです。

layerFilterSubsetString

以前よりも同じですが、(データベース内で実行) `` SubsetString``を使用

def layerFilterSubsetString(self, layer):
    return "role = 'user'"

属性の役割がある機能に制限するには、「ユーザー」に等しいです。

layerPermissions

層へのアクセスを制限します。

性質を持っているタイプ `` QgsAccessControlFilter.LayerPermissions``のオブジェクトを返します:

  • `` canRead``は `` GetCapabilities``で彼を見るために、アクセスを読みました。

  • `` canInsert``は、新しい機能を挿入することができるようにします。

  • `` canUpdate``は、機能を更新することができるようにします。

  • `` candelete``は、機能を削除することができるようにします。

Example:

def layerPermissions(self, layer):
    rights = QgsAccessControlFilter.LayerPermissions()
    rights.canRead = True
    rights.canRead = rights.canInsert = rights.canUpdate = rights.canDelete = False
    return rights

読み取り専用のアクセスのすべてを制限します。

authorizedLayerAttributes

属性の特定のサブセットの可視性を制限するために使用します。

引数の属性が表示属性の現在のセットを返します。

Example:

def authorizedLayerAttributes(self, layer, attributes):
    return [a for a in attributes if a != "role"]

「役割」属性を非表示にします。

allowToEdit

これは、機能のサブセットに編集を制限するために使用されます。

これは、 `` WFS-Transaction``プロトコルで使用されています。

Example:

def allowToEdit(self, layer, feature):
    return feature.attribute('role') == 'user'

値のユーザーを持つ属性の役割を持っているだけの機能を編集することができます。

cacheKey

QGISサーバは、あなたがこの方法で役割を返すことができる役割ごとにキャッシュを持っている能力のキャッシュを維持します。またはリターン `` None``には、完全にキャッシュを無効にします。