QGISのプラグイン作成 ラスタから値を補間して取得するサンプル


はじめに

このプラグインのサンプルでは、DEMのラスタから標高値を補完して取得するサンプルです。


使い方

ここからzipファイルをダウンロードしてインストールします。

 

f:id:Chiakikun:20200617221955p:plain

ラスタレイヤをアクティブにした状態でプラグインを実行すると、マウスカーソル位置の標高をPythonコンソールに表示します。


コード

class GetRasterPixelValueSample(QgsMapTool):

    def canvasMoveEvent(self, event):
        print(str(self.gr.getValueInterpolation(self.toMapCoordinates(event.pos()))))


    def start(self):
        self.gr = GetRasterPixelValueClass(self.iface.activeLayer())

~略~

class GetRasterPixelValueClass:

    def __init__(self, layer: QgsRasterLayer):
        self.__grs80 = pyproj.Geod(ellps='GRS80')

        # ピクセル1つのサイズ
        self.__unitX = layer.rasterUnitsPerPixelX()
        self.__unitY = layer.rasterUnitsPerPixelY()

        # ラスタのサイズ
        self.__height = layer.height()
        self.__width = layer.width()

        # ラスタの原点
        self.__provider = layer.dataProvider()
        self.__extent = self.__provider.extent()
        self.__orgX = self.__extent.xMinimum()
        self.__orgY = self.__extent.yMaximum()

        # ピクセル
        self.block = self.__provider.block(1, self.__extent, self.__width, self.__height)


    def getIndex(self, pos: QgsPointXY):

        pixelIdxX = (pos.x() - self.__orgX) / self.__unitX
        pixelIdxY = (self.__orgY - pos.y()) / self.__unitY
        if (pixelIdxX < 0) or (pixelIdxY < 0):
            return None
        pixelIdxX = int(pixelIdxX)
        pixelIdxY = int(pixelIdxY)
        if (pixelIdxX > self.__width - 1) or (pixelIdxY > self.__height - 1):
            return None

        return [pixelIdxX, pixelIdxY]


    def getGeometry(self, idxX: int, idxY: int):
        if (idxX < 0) or (idxY < 0):
            return None
        if (idxX > self.__width - 1) or (idxY > self.__height - 1):
            return None

        pixelPosX = self.__unitX * idxX + self.__orgX + self.__unitX / 2
        pixelPosY = self.__orgY - self.__unitY * idxY - self.__unitY / 2
        return [pixelPosX, pixelPosY]


    def getValueFromPos(self, pos: QgsPointXY):
        ident = self.__provider.identify(pos, QgsRaster.IdentifyFormatValue )

        if ident.isValid():
            return list(ident.results().values())[0]
        else:
            return None


    def getValueFromIdx(self, idxX: int, idxY: int):
        if (idxX < 0) or (idxY < 0):
            return None
        if (idxX > self.__width - 1) or (idxY > self.__height - 1):
            return None

        return self.block.value(idxY, idxX)


    def getValueInterpolation(self, pos: QgsPointXY):

        pixidx = self.getIndex(pos)
        if pixidx == None:
            return None
        pixelIdxX = pixidx[0]
        pixelIdxY = pixidx[1]

        pixpos = self.getGeometry(pixelIdxX, pixelIdxY)
        pixelPosX = pixpos[0]
        pixelPosY = pixpos[1]

        # 右上
        if (pixelPosX < pos.x()) and (pixelPosY < pos.y()):
            if (pixelIdxX > self.__width - 2) or (pixelIdxY - 1 < 0):
                return None
            a = self.__getXYZ__(pixelIdxX+1, pixelIdxY-1)
            b = self.__getXYZ__(pixelIdxX,   pixelIdxY-1)
            c = self.__getXYZ__(pixelIdxX,   pixelIdxY)
            d = self.__getXYZ__(pixelIdxX+1, pixelIdxY)
        # 左上
        elif (pixelPosX >= pos.x()) and (pixelPosY < pos.y()):
            if (pixelIdxX - 1 < 0) or (pixelIdxY - 1 < 0):
                return None
            a = self.__getXYZ__(pixelIdxX,   pixelIdxY-1)
            b = self.__getXYZ__(pixelIdxX-1, pixelIdxY-1)
            c = self.__getXYZ__(pixelIdxX-1, pixelIdxY)
            d = self.__getXYZ__(pixelIdxX,   pixelIdxY)
        # 左下
        elif (pixelPosX >= pos.x()) and (pixelPosY >= pos.y()):
            if (pixelIdxX - 1 < 0) or (pixelIdxY > self.__height - 2):
                return None
            a = self.__getXYZ__(pixelIdxX,   pixelIdxY)
            b = self.__getXYZ__(pixelIdxX-1, pixelIdxY)
            c = self.__getXYZ__(pixelIdxX-1, pixelIdxY+1)
            d = self.__getXYZ__(pixelIdxX,   pixelIdxY+1)
        # 右下
        elif (pixelPosX < pos.x()) and (pixelPosY >= pos.y()):
            if (pixelIdxX > self.__width - 2) or (pixelIdxY > self.__height - 2):
                return None
            a = self.__getXYZ__(pixelIdxX+1, pixelIdxY)
            b = self.__getXYZ__(pixelIdxX,   pixelIdxY)
            c = self.__getXYZ__(pixelIdxX,   pixelIdxY+1)
            d = self.__getXYZ__(pixelIdxX+1, pixelIdxY+1)

        dist1 = self.__grs80.inv(b.x(), b.y(), a.x(), a.y())[2]
        dist2 = self.__grs80.inv(b.x(), b.y(), pos.x(), b.y())[2]
        delta = (a.z() - b.z()) / dist1
        tmp1 = delta * dist2 + b.z()

        dist1 = self.__grs80.inv(c.x(), c.y(), d.x(), d.y())[2]
        dist2 = self.__grs80.inv(c.x(), c.y(), pos.x(), c.y())[2]
        delta = (d.z() - c.z()) / dist1
        tmp2 = delta * dist2 + c.z()

        dist1 = self.__grs80.inv(d.x(), d.y(), a.x(), a.y())[2]
        dist2 = self.__grs80.inv(d.x(), d.y(), d.x(), pos.y())[2]
        delta = (tmp1 - tmp2) / dist1
        inp = delta * dist2 + tmp2

        return inp


    def __getXYZ__(self, idxX: int, idxY: int):
        x   = self.__unitX * idxX + self.__orgX + self.__unitX / 2
        y   = self.__orgY - self.__unitY * idxY - self.__unitY / 2

        z = self.block.value(idxY, idxX)

        return QgsPoint(x, y, z)

90~121行目 現在の位置の周囲4ピクセルの座標を決めています。コメントで「右上」とある箇所では、現在のピクセルの上、右上、右、現在位置の4ピクセルを取得します。
145行目 その位置のピクセル値をそれぞれ取得します。
123~136行目 補間して、マウス位置の標高値を取得します。
コード全文


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

QGISのプラグイン作成 リレーションのサンプル


はじめに

このサンプルは、あるレイヤのレコードに紐付く別のレイヤのレコードを抽出するためのサンプルです。


使い方

ここからzipファイルをダウンロードしてインストールします。
インストールできたら、relation_sample.pyの以下の部分を変更します。

        self.referencedLayerName  = '〇〇〇' # 被参照のレイヤ名
        self.referencingLayerName = '□□□' # 参照元のレイヤ名
        self.referencedField  = '△△△'     # 参照元とのリンクに使うフィールド名
        self.referencingField = '×××'     # 被参照とのリンクに使うフィールド名

f:id:Chiakikun:20200606202506p:plain

プラグインを実行したままで、被参照レイヤの地物を選択すると...

 

f:id:Chiakikun:20200606202518p:plain

選択した地物とリンクする参照元レイヤの地物が選択状態になり、属性テーブルが表示されます。

 

f:id:Chiakikun:20200606202451p:plain

プラグイン実行中は、上の画像のようなリレーションが作られています。


コード

class RelationSample(QgsMapTool):

    def showChildren(self):
        parent = self.rel.referencedLayer()
        child = self.rel.referencingLayer()

        features = parent.selectedFeatures()
        if len(features) == 0:
            return

        child.removeSelection() # クリアしないと、属性テーブルに余計に表示されるから 
        for c in self.rel.getRelatedFeatures(features[0]):
            child.select(c.id())

        selectedlayer = self.iface.activeLayer() # 現在のアクティブレイヤ退避
        try:
            # このプログラム実行中は属性テーブルは選択中のフューチャーしか表示しないように設定する
            self.oldsetting = QSettings().value('/Qgis/attributeTableBehaviour')
            QSettings().setValue('/Qgis/attributeTableBehavior', 'ShowSelected')
            # テーブル表示
            self.iface.setActiveLayer(child)
            self.iface.mainWindow().findChild(QtWidgets.QAction, 'mActionOpenTable' ).trigger()
        finally:
            # 設定を戻す
            self.iface.setActiveLayer(selectedlayer)
            QSettings().setValue('/Qgis/attributeTableBehavior', self.oldsetting)


    def start(self):
        self.referencedLayer  = QgsProject.instance().mapLayersByName(self.referencedLayerName)[0]
        self.referencingLayer = QgsProject.instance().mapLayersByName(self.referencingLayerName)[0]
        self.rel = QgsRelation()

        self.rel.setReferencingLayer(self.referencingLayer.id())
        self.rel.setReferencedLayer(self.referencedLayer.id())
        self.rel.addFieldPair(self.referencingField, self.referencedField)
        self.rel.setId('適当なID')
        self.rel.setName('適当な名前')
        QgsProject.instance().relationManager().addRelation(self.rel)

        self.referencedLayer.selectionChanged.connect(self.showChildren)


    def finish(self):
        QgsProject.instance().relationManager().removeRelation(self.rel)
        self.referencedLayer.selectionChanged.disconnect(self.showChildren)
        

    def __init__(self, iface):

~略~

        self.referencedLayerName  = '〇〇〇' # 被参照のレイヤ名
        self.referencingLayerName = '□□□' # 参照元のレイヤ名
        self.referencedField  = '△△△'     # 参照元とのリンクに使うフィールド名
        self.referencingField = '×××'     # 被参照とのリンクに使うフィールド名

32~38行目 リレーションを作成します。
39行目 作成したリレーションを現在のプロジェクトに登録します。
41行目 被参照レイヤの地物を選択したときに呼ばれるメソッドを登録します。
12行目 選択した地物とリンクする参照元レイヤのレコードを選択状態にします。
18~22行目 テーブル設定を、選択地物だけしか表示できないように設定して、被参照レイヤをテーブル表示します。
45行目 プラグインが終了したら、リレーションを削除します。
コード全文


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

QGISのプラグイン作成 LayerTreeViewのサンプル


はじめに

このサンプルは、ツリービューからレイヤを選択してアクティブにした時に、そのことをPythonコンソールに表示します。


使い方

ここからzipファイルをダウンロードしてインストールします。

 

f:id:Chiakikun:20200611220049p:plain

プラグイン実行中に、レイヤウィンドウでレイヤを選択すると...

 

f:id:Chiakikun:20200611220102p:plain

選択されていたレイヤ名と、現在選択されているレイヤ名がPythonコンソールに表示されます。


コード

class LayerTreeViewSample(QgsMapTool):

    def changeLayer(self, layer):

        if (layer == None): # レイヤウィンドウに何も無い状態
            self.currentlayer = None
            self.cleared.emit()
            return

        if self.currentlayer != None:
            print(self.currentlayer.name() + 'が非アクティブになりました。')

        print(layer.name()+'がアクティブになりました。')
        self.currentlayer = layer


    def start(self):
        self.currentlayer = self.iface.layerTreeView().currentLayer()
        self.iface.layerTreeView().currentLayerChanged.connect(self.changeLayer)

~略~

    def finish(self):
        self.iface.layerTreeView().currentLayerChanged.disconnect(self.changeLayer)

~略~

18行目 現在アクティブなレイヤ名を取得しておきます。
11行目 ツリービューのアクティブレイヤが変わったら、アクティブだったレイヤ名を表示して...
13行目 アクティブになったレイヤ名を表示させます。
コード全文


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