PyQGIS スクリーンショットサンプル

下のサイトを参考に(ほとんど全部コピペですが)アクティブレイヤのスクリーンショットを保存するプラグインをPlugin Builderを使って作成してみました。

gis.stackexchange.com


最小限のサンプル

使い方

f:id:Chiakikun:20191128011658p:plain

プラグインのアイコンを押下すると表示されるダイアログの指示に従って、ベクタレイヤをアクティブにした状態で『実行』ボタンを押下すると...(背景には国土地理院の航空写真を表示させています。)

 

f:id:Chiakikun:20191128011904p:plain

このような感じで、Dドライブ直下に画像とワールドファイルが作成されます。

 

f:id:Chiakikun:20191128012124p:plain

ポリゴンを中心にスクリーンショットが作成されました。国土地理院の航空写真も拡大されています。

インストール用のzipファイルはこちらに置いておきました。

コード

screenShot_sample_dialog.py

class ScreenShotSampleDialog(QtWidgets.QDialog, FORM_CLASS):

    def __init__(self, iface, parent=None):

~中略~

    def exportMap(self):
        self.canvas.saveAsImage( "d:/{}.png".format( self.ids.pop() ) )

        if self.ids:
            self.setNextFeatureExtent()
        else:
            self.canvas.mapCanvasRefreshed.disconnect( self.exportMap )


    def setNextFeatureExtent(self): # この関数が終わるとself.exportMapが呼ばれます(リフレッシュが終わるので)
        self.canvas.zoomToFeatureIds( self.layer, [self.ids[-1]] ) # -1 しているのは、exportMapでpopしているから
        self.canvas.zoomScale(250000) # 縮尺を1:250000に設定します 


    def pushExec(self):
        self.layer = self.iface.activeLayer()
        if type(self.layer) is not qgis.core.QgsVectorLayer:
            return
        self.ids = self.layer.allFeatureIds()

        self.canvas.mapCanvasRefreshed.connect( self.exportMap )

        self.setNextFeatureExtent() # setNextFeatureExtent -> exportMap -> ...のループに突入

        self.close()

7行~18行目 setNextFeatureExtentでズーム→リフレッシュイベントが発生→exportMap→setNextFeatureExtent...のループでフューチャーのスクリーンショットを撮っています。普通のforループですと、ズームしたあとに地物の再描画が終わる前にスクリーンショットが撮られてしまうことがあります。

 


フューチャー毎にスクリーンショット

使い方

f:id:Chiakikun:20191203001817p:plain

少しインターフェースを変えて、画像の保存先と縮尺を設定できるようにしてみました。

撮影するベクタレイヤをアクティブにしてアイコンを押下すると上のようなインターフェースが表示されるので、画像保存先のテキストボックス右側にあるボタンを押下して、保存先フォルダを選択します。

縮尺を設定したら『実行』ボタンを押下します。

 

f:id:Chiakikun:20191203001840p:plain

どのフューチャーを中心に撮っているかがわかるように、そのフューチャーが赤くなっているスクリーンショットが保存先に出力されます。

インストール用のzipファイルはこちらに置いておきました。

コード

screenShot_Sample2_dialog.py

class ScreenShotSample2Dialog(QtWidgets.QDialog, FORM_CLASS):
    def __init__(self, parent=None):
    
    ~中略~

        # 縮尺リスト
        self.scaleList.addItem('2500')
        self.scaleList.addItem('10000')
        self.scaleList.addItem('50000')
        self.scaleList.addItem('100000')
        self.scaleList.addItem('250000')


    def exportMap(self):
    
    ~中略~

    def setNextFeatureExtent(self):
        # スクリーンショット撮るフューチャー
        rule = self.root_rule.children()[0].clone()
        rule.setFilterExpression('$id = ' + str(self.ids[-1]))
        self.root_rule.insertChild(1,rule) # この時点でルールは「赤(対象idは前のid)、赤(対象idはids[-1])、そのまま」
        self.root_rule.removeChildAt(0)    # この時点でルールは「赤(対象idはids[-1])、そのまま」
        self.layer.setRenderer(self.renderer)
        self.layer.triggerRepaint()

        self.canvas.zoomToFeatureIds( self.layer, [self.ids[-1]] )
        self.canvas.zoomScale(int(self.scaleList.currentText()))


    def pushExec(self):
    
    ~中略~

        # スタイル設定 画像にするフューチャーを赤くしたいので... 「https://gis.stackovernet.com/ja/q/54438」を参考にしました
        self.prerenderer = self.layer.renderer().clone()
        symbol = qgis.core.QgsSymbol.defaultSymbol(self.layer.geometryType())
        self.renderer = qgis.core.QgsRuleBasedRenderer(symbol)
        style_rules = (
            ('Target', '$id = 0', '#ff0000'), # $idが0なら赤
            ('Other', 'ELSE', self.prerenderer.symbol().color().name()), # それ以外はそのまま      
        )
        self.root_rule = self.renderer.rootRule()
        for label, expression, color_name in style_rules:
            rule = self.root_rule.children()[0].clone() # ラベル、フィルタ、色以外はデフォルトを使いたいのでcloneしています
            rule.setLabel(label)
            rule.setFilterExpression(expression)
            rule.symbol().setColor(QColor(color_name))
            self.root_rule.appendChild(rule) # この時点でルールは「デフォルト、赤、そのまま」
        self.root_rule.removeChildAt(0)      # この時点でルールは「赤、そのまま」
        self.layer.setRenderer(self.renderer)
        self.layer.triggerRepaint()

        # スクリーンショット開始
        self.canvas.mapCanvasRefreshed.connect( self.exportMap )
        self.setNextFeatureExtent() # setNextFeatureExtent -> exportMap -> ... のループ

36行目~52行目 シンボロジ『ルールに基づいた』設定の先頭ルールをコピーして、これから撮影しようとしているフューチャーは赤くなるように設定しています。コピー元は設定後に削除しています。

 

21行目~23行目 シンボロジ『ルールに基づいた』で、指定したidは赤く、それ以外はそのままの色になるように設定しています。


フューチャー毎にスクリーンショット

使い方

f:id:Chiakikun:20200229153014p:plain

①ラスタレイヤをいくつか開いた状態でプラグインを実行します。

 

f:id:Chiakikun:20200229153035p:plain

②ダイアログが表示されるので赤丸部分のボタンを押下します。

 

f:id:Chiakikun:20200229153051p:plain

③上のようなダイアログが表示されるので、スクリーンショットを撮りたいレイヤを選択します。OpenStreatMapもラスタではありますが、サンプルプラグインでは表示されないようにしています。選択したら『OK』ボタンを押下します。

 

f:id:Chiakikun:20200229153111p:plain

④ラスタレイヤの選択ダイアログで選択し終えたら、『実行』ボタンを押下します。

プラグインではDドライブ直下に画像が保存されるようになっています。

 

インストール用のzipファイルはこちらに置いておきました。

コード

screenshot_sample3_dialog.py

class ScreenShotSample3Dialog(QtWidgets.QDialog, FORM_CLASS):

~中略~

    def exportMap(self):
        self.iface.setActiveLayer(self.layer)
        self.canvas.saveAsImage('d:/' + self.iface.activeLayer().name() + '.png')

        if len(self.selectedLayers) > self.count:
            self.setNextFeatureExtent()
        else:
            self.canvas.mapCanvasRefreshed.disconnect( self.exportMap )
            self.close()


    def setNextFeatureExtent(self):
        self.layer = qgis.core.QgsProject.instance().mapLayersByName(self.selectedLayers[self.count])[0]
        self.iface.setActiveLayer(self.layer)
        self.iface.zoomToActiveLayer() 	

        self.count += 1

~中略~
  

ほとんど最小限のサンプルと変わらないですね。フューチャー毎の部分がレイヤ毎になっただけです。



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