QGISのプラグイン作成 Matplotlibを使ってグラフを作成するサンプル


はじめに

QGISプラグインでグラフを作成し、画像として保存します。
グラフにするデータとして、ここではDEMから取得した標高値を使います。


このプラグインの使い方

ここからダウンロードできるzipをインストールして、graph_sample.pyの以下の部分を適当に編集してください。

        self._llcrs = 4326
        self._xycrs = 2451 # 千葉県のDEMでテストしたので9系になってます。
        self._savedir = 'c:\\users\\〇〇\\desktop\\pic\\' # 横断図の保存先
        self._odanlinespan   = 100 # 横断線の間隔
        self._odanlinelength = 200 # 横断線の片側の長さ
        self._odanpointspan  = 10 # 横断線上のサンプリング間隔

f:id:Chiakikun:20201105234335p:plain

DEMのラスタをアクティブにした状態でプラグインを実行すると、10秒ほど経ってマウスカーソルが✚になり、ラバーバンドでラインが描画できるようになります。描画したら右クリックで確定します。

 

f:id:Chiakikun:20201105234357p:plain

しばらく(描画したラインの長さによっては大分)待たされますが、描画したラインを横断するように点が作成されます。各点にはDEMから取得した標高が入っています。

 

f:id:Chiakikun:20201105234441p:plain

各点の標高値を使って、グラフが作成されます。


f:id:Chiakikun:20210505105803p:plain

このような画像になります。


コード

~略~
import matplotlib.pyplot as plt
import numpy as np
from .rubberband import RubberBand
from .getrasterpixelvalue import GetRasterPixelValue
import datetime
import math

class GraphSample(QgsMapTool):

    # lineLLを垂直に横断する点列を作成する
    def addOdansen(self, lineLL, key):

~略~

        # startpnt、endpntが成す線分の、startpntからlength分進んだ位置を中心に、線分を横断する線を作成する。
        def odansen(startpnt, endpnt, length, cnt):

~略~

            for i in range(-self._odanlinelength, self._odanlinelength + self._odanpointspan, self._odanpointspan):
                xypnt = QgsGeometry().fromPointXY(destination(center, rad, i))
                llpnt = transformCRS(xypnt, self._xycrs, self._llcrs)

                elev = self.gr.getValueInterpolation(llpnt.asPoint())
                self.addFeature(self.hol, llpnt, [key, cnt, i, elev])

~略~


    def setFeature(self, geom):
        def getNumber(feature):
            return feature['pointcount']

        key = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 縦断線と横断線を紐付けるキー
        self.addFeature(self.ver, geom, [key]) # 縦断線レイヤに、描画したラインを追加
        self.addOdansen(geom, key)             # 描画したラインを横断する点列を横断戦レイヤに追加
        
        # グラフ作成
        features = list(self.hol.getFeatures(QgsFeatureRequest().setFilterExpression('"key"='+'\'' + key + '\'')))
        linenumbers = sorted(set([f['linecount'] for f in features]))
        for linenumber in linenumbers:
            linenode = sorted([f for f in features if f['linecount'] == linenumber], key=getNumber) # 'pointcount'の値順に並び変える

            fig, ax = plt.subplots()
            y = np.array([l['elev'] for l in linenode])
            x = np.array([l['pointcount'] for l in linenode])
            ax.set_title(str(linenode[0]['key']) + '_' + str(linenumber))
            ax.plot(x, y, '-')
            plt.savefig(self._savedir + str(linenode[0]['key']) + '_' + str(linenumber) + '.png')


    # 一時レイヤ作成
    def createTemporaryLayer(self, layername, type, fieldsstr):

~略~


    # レイヤにレコード追加
    def addFeature(self, layer, geometry, attrs):

~略~


    def start(self):
        # このプログラム使うときはこの辺を調整してください
        self._llcrs = 4326
        self._xycrs = 2451 # 千葉県のDEMでテストしたので9系になってます。
        self._savedir = 'c:\\users\\me\\desktop\\pic\\' # 横断図の保存先
        self._odanlinespan   = 100 # 横断線の間隔
        self._odanlinelength = 200 # 横断線の片側の長さ
        self._odanpointspan  = 10  # 横断線上のサンプリング間隔

        if self.iface.activeLayer() is not qgis.core.QgsRasterLayer:

~略~

        self.gr = GetRasterPixelValue(self.iface.activeLayer())

        maptool = RubberBand(self.iface, self.canvas, QgsWkbTypes.LineGeometry)
        maptool.getObject.connect(self.setFeature)

        self.ver = self.createTemporaryLayer('縦断線', 'LineString', '&field=key:string')
        self.hol = self.createTemporaryLayer('横断線', 'Point',      '&field=key:string&field=linecount:integer&field=pointcount:integer&field=elev:double')

        self.canvas.setMapTool(maptool)
        self.canvas.mapToolSet.connect(self.unsetTool) # このサンプル実行中に他のアイコンを押した場合

~略~

7行目 描画したラインを横断する点を、点が格納されているレイヤ(self.hol)から取得してきます。

8行目 横断線の本数を取得しています。

9行目 7行目で取得した点の中から、作成するグラフに使う点を取得しています。

 12~16行目 グラフを準備します。縦軸には標高、横軸には描画したラインからの距離を使います。

17行目 作成したグラフを保存しています。

ソース全文


最後までご覧頂き、ありがとうございました