waigani's diary

QGISを中心にFOSS4Gをいじくる

QGISのSVG塗りつぶしパターンを属性から作ってみる

FOSS4G 2013 Tokyoお疲れ様でした

10/31-11/2行われていたFOSS4G 2013 Tokyoに参加していました。運営のみなさま疲れさまでした。引き続き11/6-7のFOSS4G 2013 Osaka頑張ってください。Ust中継見てようと思います。

今回参加しての収穫の1つが、下記の本を紹介してもらったことでした。

Information Graphics

Information Graphics

  • 作者: Sandra Rendgen,Paolo Ciuccarelli,Richard Saul Wurman,Simon Rogers,Julius Wiedemann
  • 出版社/メーカー: Taschen America Llc
  • 発売日: 2012/05/27
  • メディア: ハードカバー
  • クリック: 2回
  • この商品を含むブログ (1件) を見る
家に帰ってさっそくポチッとしてしまいました。まだ手元に届いてないのですが残念です。

面を属性で埋める

"Information Graphics"の中で紹介されていた、文字列で作った地図が気になっています。例えば行政区域のポリゴンがあったとしてその名称で、"あさひかわあさひかわあさひかわ"のようなパターンで埋めます。うまく伝えられなくてごめんなさい。
QGISで旨いこと出来ないかなと思い、少しだけやってみました。
制約が多いので、あくまで実験的のものです。他にいい方法あるよという方、ご指摘お待ちしております。

使用するデータ

natural earthのデータを使用します。
1:50m Cultural Vectorsの"Admin 0 – Countries"になります。

結果

先に結果。ポリゴンの属性に入っている国名からSVGファイルを作成して、スタイルに指定しています。本当はもう少し1文字ずつサイズとか変えながらやれると格好いいと思うのですが、そこまでやれないので単純なパターンで。当然"Information Graphics"で紹介されている地図はもっともっと格好いいです。


書いたソース

QGIS2.0の、pythonコンソールから実行します。"エディタの表示"をしておいて、ソースを貼り付けて実行しています。
前提条件として、

  • "sovereignt"カラムの値を描画することにしています。他のカラムの場合utf8以外のコードが入っているとエラーが出たため、そうしています。
  • レイヤーは選択済み

としています。
処理としては、

  • 属性値のリストを作っておく
  • 属性値毎のSVGファイルを作成(temporaryファイルですが消さず)
  • symbolにSVG塗りつぶしを入れる
  • "ルールに基づいた"スタイルにフィルターと合わせてsymbolをセット
  • 全ての属性値に対するルールの設定が終わったら、スタイルをrendererに入れてlayerに設定
  • 再描画

といった流れです。

# -*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
import tempfile

#レイヤーが選択されていることが前提
layer = iface.activeLayer()

#描画する属性フィールド
key = "sovereignt"

#空の"ルールに基づいた"スタイル作成
rule = QgsRuleBasedRendererV2.Rule(None)

#指定の属性フィールドの値リストを作成
attrList = []
features = layer.getFeatures()
for feature in features:
    if feature[key] not in attrList:
        attrList.append(feature[key])

for attr in attrList:
    #指定の属性を入れたSVGファイルを作成
    fillPattern = """<?xml version="1.0" standalone="no"?>
        <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
         "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">"""
    fillPattern += '<svg width="%.1fpx" height="1px" ' % (len(attr)*0.5)
    fillPattern += 'xmlns="http://www.w3.org/2000/svg" version="1.1">'
    fillPattern += '<text x="0" y="1" font-family="serif" font-size="1" fill="blue" >'
    fillPattern += '%s' % attr
    fillPattern += '</text></svg>'
    
    tempSvg = tempfile.NamedTemporaryFile(mode="w+t", delete=False)
    tempSvg.write(fillPattern)
    tempSvg.close()
    
    #空のシンボルを作成
    symbol = QgsSymbolV2.defaultSymbol(QGis.Polygon)
    symbol.deleteSymbolLayer(0)
    
    #属性を入れたSVGを指定して、SVG塗りつぶしを作成
    fillLayer = QgsSVGFillSymbolLayer(tempSvg.name, len(attr)*2)
    
    #シンボルに塗りつぶしを追加
    symbol.appendSymbolLayer(fillLayer)
    
    #指定の属性カラムによるフィルタを設定して、ルールを作成、追加
    ruleAdd = QgsRuleBasedRendererV2.Rule(symbol, label=attr, filterExp="\"%s\" = '%s'" % (key, attr), description=attr)
    rule.appendChild(ruleAdd)
    
renderer = QgsRuleBasedRendererV2(rule)

layer.setRendererV2(renderer)
iface.mapCanvas().refresh()

iface.legendInterface().refreshLayerSymbology(layer)

ToDo

続けるかわかりませんが、課題。

  • SVGファイル残すので一時ファイルがいっぱいで格好悪い
  • SVGに凝るともう少し格好良く表現出来るかな
  • そもそもPyQtでやることに無理がある?やりたいなら本体いじるべきか?

以上