QGIS Print Composerの凡例の凡例記号を変更することは可能ですか?


9

非常に複雑なデータ定義シンボルを含むレイヤーがいくつかあります。Print Composerで凡例を使用すると、これらの記号が正しく描画されません。

pyqgisで凡例シンボルを変更する方法はありますか?したがって、これらのレイヤーのデフォルトの凡例シンボルの代わりにカスタムのPNGまたはSVG画像を使用できますか?

Print Composerにプッシュボタンを追加する方法と、それを関数に接続する方法を知っています。Legend-Settingsにボタンを追加して、自動生成された凡例アイコンをカスタム画像に置き換えることができるようにしたいと思います。だから私がまだ必要なのは、どのようにしてpyqgis / pyqtで凡例シンボルにアクセスできるか、そしてそれらをQLabelなどのQImageで置き換える方法の情報ですか?

シンボルの変更に使用されるボタンの非常に基本的なモックアップ:

ここに画像の説明を入力してください

自動生成された凡例:

ここに画像の説明を入力してください

カスタム凡例記号付きの凡例:

ここに画像の説明を入力してください

Print Composerで凡例のアイテムにアクセスする方法はすでにわかっていますが、シンボル自体にアクセスする方法はまだわかりません。

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

activeComposer = iface.activeComposers()

for item in activeComposer:
    if item.composerWindow().windowTitle()=='test':
        for i in item.items():
            if isinstance(i,QgsComposerLegend):
                #print i
                #print i.model()
                legend = i
                for i in xrange(legend.modelV2().rowCount()):
                    posteleg=legend.modelV2().index(i, 0)
                    print posteleg.data()

編集1:

また、legend-treeのQIcon-Objectsにアクセスする方法も見つけましたが、まだスワップできません。

def run(self):

        activeComposer = self.iface.activeComposers()
        #print(self.resolve('icon.png'))
        for item in activeComposer:
            if item.composerWindow().windowTitle()=='test':
                for i in item.items():
                    if isinstance(i,QgsComposerLegend):
                        legend = i

                        layerIcon = QIcon(os.path.join(os.path.dirname(__file__), "icon.png"))

                        for i in xrange(legend.modelV2().rowCount()):
                            posteleg=legend.modelV2().index(i, 0)
                            posteleg.model().iconGroup().swap(layerIcon)
                            print posteleg.data()

ここに画像の説明を入力してください これは、多くのシンボルレイヤーの組み合わせのシンボルを見ることができる実際の例です。 これは、次のような凡例になります。 ここに画像の説明を入力してください

凡例に適切なシンボルが必要なため、シンボルのスクリーンショットを作成し、トリミングして、凡例の画像として使用します。

自動生成されたシンボルをカバーする別の画像を凡例の上に重ねることができることはわかっていますが、凡例のシンボルをカスタム画像に置き換えることができる「よりクリーンな」ソリューションが欲しいです。

編集2:

その間に、凡例のエントリにアクセスする別の方法を見つけました。

from qgis.core import QgsLegendRenderer, QgsComposerLegendStyle

compDict = {}
for comp in iface.activeComposers():
    # workaround to get name: read it from window title
    compDict[comp.composerWindow().windowTitle()] = comp.composition()
if "mycomposername" in compDict:
    itemLlegend = compDict["mycomposername"].getComposerItemById("mylegend_id")
    if itemLlegend:
        print itemLlegend

tree_layer_layer =  itemLlegend.modelV2().rootGroup().children()
for item in tree_layer_layer:
        if item.layerName()=="MyLayername":
            print "match"
            QgsLegendRenderer.setNodeLegendStyle(item, QgsComposerLegendStyle.Group)

これにより、QgsLayerTreeLayerオブジェクトにアクセスでき、凡例のスタイル(グループ、サブグループ、非表示)を切り替えることができます。しかし、レジェンドシンボルにアクセスする方法はまだわかりません。何か案は?


1
QGISでこれに遭遇すると、通常、凡例に表示したいシンボルを持つ追加のレイヤーを作成します(通常、新しいデータソースではなく、既存のレイヤーの単純な複製)。次に、コンポーザーでマップウィンドウを設定し、レイヤーをロックします。レイヤーがロックされた後、「偽の」レイヤーをオンにして、凡例に追加できます。これは純粋に回避策であり、PyQGISにはありませんが、おそらく「必要なもの」の「偽の」レイヤーをエミュレートするいくつかの方法があるでしょうか?
Nate Wanner 2017

回答:


10

このトピックは多くの議論をカバーするので、あなたが探しているものをよく理解していることを願って、SVGシンボルレイヤーにのみ焦点を当てます(執筆中に答えの長さを理解していなかったので、申し訳ありません)しかし、それがより明確になることを願っています)。


環境

1)シンボルレイヤークラス

SVG形式では、次のシンボルレイヤークラスを使用できます。

  • QgsSvgMarkerSymbolLayerV2は、指定されたSVG画像を使用してポイントジオメトリを表示します(ポイントレイヤーで機能します)。
  • QgsSVGFillSymbolLayerは、繰り返されるSVG画像を使用してポリゴンジオメトリの内部を描画します(ポリゴンレイヤーで機能します)。

シンボルレイヤーを作成するための一般的なアプローチは、プロパティのディクショナリで初期化することです。

次の方法で、新しいシンボルレイヤーを初期化して、そのデフォルトプロパティを確認できます。

symbol_layer = QgsSvgMarkerSymbolLayerV2()
for k,v in symbol_layer.properties().iteritems():
    print k, ':', v

そこに格納されているすべてのプロパティを取得します。

outline_width : 0.2
outline_color : 0,0,0,255
angle : 0
name : crosses/Star1.svg
scale_method : diameter
color : 0,0,0,255
size_unit : MM
horizontal_anchor_point : 1
size_map_unit_scale : 0,0,0,0,0,0
outline_width_unit : MM
offset : 0,0
offset_map_unit_scale : 0,0,0,0,0,0
outline_width_map_unit_scale : 0,0,0,0,0,0
size : 4
vertical_anchor_point : 1
offset_unit : MM

プロパティを編集する場合は、クラスのヘルプから呼び出すことができるメソッドを使用できます(例:help(QgsSvgMarkerSymbolLayerV2)Pythonコンソールで実行)。後でメソッドの使用例を確認します。

完全を期すために、プロパティのディクショナリを使用してシンボルレイヤーを初期化することもできます(たとえば、こちらを参照)。ただし、最初のアプローチを心から優先して使用します。

2)レンダラーを作成する

シンボルレイヤーを作成して(最終的には編集して)使用するには、適切なレンダラーを作成し、そのレンダラーをマップレイヤーに割り当てる必要があります。

レイヤーの既存のレンダラーにアクセスするには:

renderer = layer.rendererV2()

使用可能なレンダラータイプのリストを取得するには、以下を使用します。

renderer_types = QgsRendererV2Registry().renderersList()

あなたのケースでは、カテゴリー化されたシンボルレンダラーを扱う必要があります。前に述べたように、レンダラーを作成してレイヤーに割り当てる必要があります。

# define the lookup: value -> (color, label)
landuses = {'Agriculture': ('#d3a151', 'Agriculture'), 'Natural': ('#175dcd', 'Natural'),}

# create a category for each item in landuses
categories = []
for landuse_name, (color, label) in landuses.items():
    symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
    symbol.setColor(QColor(color))
    category = QgsRendererCategoryV2(landuse_name, symbol, label)
    categories.append(category)

# create the renderer and assign it to the layer
expression = 'landuse' # field name
renderer = QgsCategorizedSymbolRendererV2(expression, categories) # categorized symbol renderer
layer.setRendererV2(renderer) # assign the renderer to the layer

3)シンボルレイヤーの変更

Categorizedシンボルレンダラーのいくつかのシンボルは、呼び出し可能ですsymbols()(リストを返します)。

for symb in renderer.symbols():
    print symb

<qgis._core.QgsMarkerSymbolV2 object at 0x0E1FF760>
<qgis._core.QgsMarkerSymbolV2 object at 0x0E1FF7B0>

symbol以前に定義した内の特定のシンボルレイヤーを置き換える場合は、そのインデックスを知っていれば、次のようにレンダラーに通知するだけです。

renderer.symbols()[0].changeSymbolLayer(0, new_symbol)

ここで、[0]分類グループの最初の項目を示しています。

解決

最後に、今学んだことを適用しましょう!

以前に定義した土地利用を保存するこのポリゴンレイヤーで作業するとします。

ここに画像の説明を入力してください

特定のSVG画像を使用して、農地のデフォルトのパターン(「landuse」グループの位置が1位)を変更する場合は、このコードを実行できます(カスタムSVGの追加方法については、こちらをお読みください)道):

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

# define the lookup: value : (color, label)
landuses = {'Agriculture': ('#d3a151', 'Agriculture'), 'Natural': ('#175dcd', 'Natural'),}

# create a category for each item in landuses
categories = []
for landuse_name, (color, label) in landuses.items():
    symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
    symbol.setColor(QColor(color))
    category = QgsRendererCategoryV2(landuse_name, symbol, label)
    categories.append(category)

# create the renderer and assign it to the layer
expression = 'landuse' # field name
renderer = QgsCategorizedSymbolRendererV2(expression, categories)

activeComposer = iface.activeComposers()
for item in activeComposer:
    if item.composerWindow().windowTitle()=='test':
        for i in item.items():
            if isinstance(i,QgsComposerLegend):
                legend = i
                for k in xrange(legend.modelV2().rowCount()):
                    posteleg=legend.modelV2().index(k, 0)
                    layer = QgsMapLayerRegistry.instance().mapLayersByName( posteleg.data() )[0]
                    if k == 0: # k is the position of the layer of interest in Legend (NOT in the Layers Panel)
                        svg_location = 'C:/path_to_svg/smile.svg'
                        new_symbol = QgsSVGFillSymbolLayer()
                        new_symbol.setSvgFilePath(svg_location)
                        new_symbol.setPatternWidth(7.0)
                        #... If you want to set additional parameters, type help(QgsSVGFillSymbolLayer) in the Python Console
                        renderer.symbols()[1].changeSymbolLayer(0, new_symbol)
                    layer.setRendererV2(renderer)
                    layer.triggerRepaint()

これは結果になります:

ここに画像の説明を入力してください

上記のコードは非常に大まかなものですが、例の具体的な解決策が必要かどうか、またはより一般的な説明が必要かどうかわからなかったため、コード自体を精緻化するのではなく、利用可能なツールに注目することを優先しました(きっと特定のニーズに合わせて少し編集できます!)。


あなたの幅広い回答に感謝しますが、レイヤー自体ではなく、コンポーザーの凡例のシンボル(のみ)を変更する解決策を探しています。上記の質問をもう1つ例を挙げて
少し

@markgraeflerland(実際に新しい)編集された質問で説明されている問題について誰も考えていなかったと思います。凡例の画像がレイヤーに表示されるものを再現しないことを指定したことはありません(私が行ったように、凡例の項目を画像に置き換えたかったようです)。誠にありがとうございます。私の意見では、元の質問は非常に誤解を招くものであり、私の投稿は実際にそれに対する答えを出そうとしました。時間を無駄にして申し訳ありませんが、ググリングするときに他の人が興味を持つ可能性があるので、その答えは削除しません。あなたの研究で頑張ってください!
mgri 2017

2
私の質問の最初の文に「Print Composerで凡例を使用すると、これらの記号が正しく描画されない」と書いたので、凡例の画像が私が再現したものを再現しないことを指定しなかったとは思わないレイヤーを参照してください。
markgraeflerland 2017

5

あるいは、Pythonコーディングなしで、凡例の作成専用の新しいレイヤーグループを作成することでこれを解決しました。ここに、好きなサイズと色で好きなものを配置できます。このようにPrint Composerで、凡例アイテムから実際のデータレイヤーを削除し、凡例レイヤーグループのみを保持しました。

これは、実際に印刷されたマップでは発生しないケースを表すことができる凡例を作成する必要がある場合に特に実用的です。

編集:2番目の編集について、構成されたシンボルが正しく表示されない場合、「C」または「G」が実際にフィールドに相対的である、または方向など、シンボルを定義するいくつかの変数がありますか?その場合、QGISは表示したいものを推測できないため、これらのパラメーターの値なしですべてを表示します。回避策は、変数の代わりにいくつかの固定値を使用して記号を保存することです。このように、私は伝説のアイテムを表示し、このデフォルトを交換することができ:ここに画像の説明を入力してくださいこの1で私の必要性をフィッティングここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.