読者です 読者をやめる 読者になる 読者になる

waigani's diary

QGISを中心にFOSS4Gをいじくる

QGISでTypographicMap

続きです

これで本当に最後の予定です。
せっかく2回やったのでまとめを行います。
QGISのSVG塗りつぶしパターンを属性から作ってみる
QGISのSVGマーカーを属性から作ってラインに置く

使用するデータ

基盤地図情報ダウンロードサービスから縮尺レベル2500の行政区画・軌道の中心線・道路縁を使用しました。
付属のツールでshapefileへの変換を行い、カラム名称を日本語からアルファベットに変換はしています。
行政区画についてはnameカラムに行政名が入るように、軌道の中心線・道路縁についてはsortカラムに種別が入るようにしています。

結果

スケールでフィルタしているので、ひいているときは行政区画だけ表示。


行政区画だけ表示、もう少し拡大。


拡大していくと道路も表示。完全にラインに沿っての表示は出来ていない。ライン上にマーカーを置いているので、はみ出している文字が出てきます。改善したいですが、この方法だと無理ですね。


五稜郭

書いたソース

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

  • ラインは"sort"カラム、ポリゴンは"name"カラムの属性を描画

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

  • レイヤーを順次アクセス
  • ベクトルレイヤのうち、ラインとポリゴンのみが対象
  • 属性値のリストを作っておく
  • 属性値毎のSVGファイルを作成(temporaryファイルですが消さず)
  • 図形種別にあわせたシンボル作成、色はランダム、ポリゴン塗りつぶしについては角度もランダム
  • "ルールに基づいた"スタイルにフィルターと合わせてラインシンボルをセット
  • 全ての属性値に対するルールの設定が終わったら、スタイルをrendererに入れてlayerに設定
  • 再描画

といった流れです。
あいかわらず変なやり方なので、スタイルの設定の参考程度に。

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

#描画サイズ
lineSymbolSize = 3
polySymbolSize = 4

def makePolySymbol(attr, svg, size):
    #空のシンボルを作成
    symbol = QgsFillSymbolV2()
    symbol.deleteSymbolLayer(0)
    
    #属性を入れたSVGを指定して、SVG塗りつぶしを作成
    fillLayer = QgsSVGFillSymbolLayer(svg.name, len(attr)*size)
    subSymbol = fillLayer.subSymbol()
    subSymbol.deleteSymbolLayer(0)
    
    #シンボルに塗りつぶしを追加
    symbol.appendSymbolLayer(fillLayer)
    #適当に回転させる
    symbol.setAngle(random.randint(0,90))
    return symbol

def makeLineSymbol(attr, svg, size):
    #SVG読み込み
    markerSymbolLayer = QgsSvgMarkerSymbolLayerV2 (svg.name, len(attr)*size)
    
    #マーカーライン作成、SVGシンボルセット
    markerLine = QgsMarkerLineSymbolLayerV2()
    svgSymbol = markerLine.subSymbol()
    svgSymbol.deleteSymbolLayer(0)
    svgSymbol.appendSymbolLayer(markerSymbolLayer)
    markerLine.setInterval(len(attr)*size)

    symbol = QgsLineSymbolV2()
    symbol.deleteSymbolLayer(0)
    symbol.appendSymbolLayer(markerLine)
    return symbol

#レイヤーを順次アクセス
layers = iface.legendInterface().layers()
for layer in layers:
    if layer.type() != layer.VectorLayer:
        continue

    if layer.geometryType() != QGis.Line and layer.geometryType() != QGis.Polygon:
        continue
   
    #描画する属性フィールド
    key = "sort" if layer.geometryType() == QGis.Line else "name"

    #空の"ルールに基づいた"スタイル作成
    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ファイルを作成
        svgPattern = """<?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">"""
        svgPattern += '<svg width="%dpx" height="1px" ' % len(attr)
        svgPattern += 'xmlns="http://www.w3.org/2000/svg" version="1.1">'
        svgPattern += '<rect x="0" y="0" width="%d" height="1" fill="#FFFFFF" />' % len(attr)
        svgPattern += '<text x="0" y="1" font-family="MS-Mincho" font-size="1" fill="rgb(%i,%i,%i)" >' % (random.randint(0,255), random.randint(0,255), random.randint(0,255))
        svgPattern += '%s' % attr.encode('utf-8')
        svgPattern += '</text>'
        svgPattern += '</svg>'
    
        tempSvg = tempfile.NamedTemporaryFile(mode="w+t", delete=False)
        tempSvg.write(svgPattern)
        tempSvg.close()

        symbol = makeLineSymbol(attr, tempSvg, lineSymbolSize) if layer.geometryType() == QGis.Line else makePolySymbol(attr, tempSvg, polySymbolSize)

        #指定の属性カラムによるフィルタを設定して、ルールを作成、追加
        #ラインとポリゴンで表示スケール変更
        scaleMax = 2500 if layer.geometryType() == QGis.Line else 200000
        ruleAdd = QgsRuleBasedRendererV2.Rule(symbol, label=attr, filterExp="\"%s\" = '%s'" % (key, attr), description=attr, scaleMaxDenom=scaleMax)
        rule.appendChild(ruleAdd)
    
    renderer = QgsRuleBasedRendererV2(rule)

    layer.setRendererV2(renderer)

iface.mapCanvas().refresh()

iface.legendInterface().refreshLayerSymbology(layer)