ベクターレイヤを使う

このセクションではベクタレイヤに対して行える様々な操作について紹介していきます.

属性に関する情報を取得します

:class:`QgsVectorLayer`インスタンス上:func:`pendingFields`を呼び出すことにより、ベクトルレイヤーに関連付けられたフィールドに関する情報を取得できます:

# "layer" is a QgsVectorLayer instance
for field in layer.pendingFields():
    print field.name(), field.typeName()

ノート

QGIS 2.12から出発する、こともある:class:`QgsVectorLayer`中の:func:`fields()`もある、:func:`pendingFields`の別名である。

フィーチャの選択

QGISデスクトップでは、地物はいろいろな方法で選択できます。ユーザーは地物をクリックしても、地図キャンバス上で長方形を描いても、表現フィルタを使用てもよいです。 選択された地物は、選択に関してユーザーの注意をひくために、通常は異なる色(デフォルトは黄色です)で強調表示されます。 プログラム的に地物を選択するとか、デフォルト色を変更すると役に立つこともあります。

選択色を変更するには、次の例に示すように、 :class:`QgsMapCanvas`の:func:`setSelectionColor() `メソッドを使用できます:

iface.mapCanvas().setSelectionColor( QColor("red") )

所定のレイヤーのために選択した地物のリストに地物を追加するには、:func:`setSelectedFeatures()`を、地物のIDのリストをそれに渡しつつ呼び出すことができます:

# Get the active layer (must be a vector layer)
layer = iface.activeLayer()
# Get the first feature from the layer
feature = layer.getFeatures().next()
# Add this features to the selected list
layer.setSelectedFeatures([feature.id()])

選択を解除するため、空のリストを通ります。

layer.setSelectedFeatures([])

ベクターレイヤの反復処理

ベクターレイヤのフィーチャへの反復処理はもっとも頻繁に行う処理の一つです。次の例はこの処理を行う基本的なコードで、各フィーチャのいくつかの情報を表示します。 layer はQGSVectorLayerオブジェクトとしています。

iter = layer.getFeatures()
for feature in iter:
    # retrieve every feature with its geometry and attributes
    # fetch geometry
    geom = feature.geometry()
    print "Feature ID %d: " % feature.id()

    # show some information about the feature
    if geom.type() == QGis.Point:
        x = geom.asPoint()
        print "Point: " + str(x)
    elif geom.type() == QGis.Line:
        x = geom.asPolyline()
        print "Line: %d points" % len(x)
    elif geom.type() == QGis.Polygon:
        x = geom.asPolygon()
        numPts = 0
        for ring in x:
            numPts += len(ring)
        print "Polygon: %d rings with %d points" % (len(x), numPts)
    else:
        print "Unknown"

    # fetch attributes
    attrs = feature.attributes()

    # attrs is a list. It contains all the attribute values of this feature
    print attrs

属性のアクセス

属性は、それらの名称によって参照されます。

print feature['name']

あるいは、属性はインデックスに参照されます。これは名称を使うよりもやや高速です。たとえば、新しい属性を取得するためです:

print feature[0]

選択されたフィーチャへの反復処理

地物を選択する必要のみある場合、 ベクタレイヤから :func: selectedFeatures メソッドを使用できます。

selection = layer.selectedFeatures()
print len(selection)
for feature in selection:
    # do whatever you need with the feature

別のオプションは、処理:func:`features`メソッドです:

import processing
features = processing.features(layer)
for feature in features:
    # do whatever you need with the feature

デフォルトでは、これは、選択がない場合はレイヤー中のすべての地物について、そうでない場合は選択されたすべての地物について繰り返します。 このふるまいは選択を無視するように「処理」オプションで変更できることに注意。

一部のフィーチャへの反復処理

もし所定の範囲内に含まれフィーチャのように、レイヤ中の所定のフィーチャにのみ処理を行いたい場合、 QgsFeatureRequest オブジェクトを getFeatures() に加えます。下記が例になります。

request = QgsFeatureRequest()
request.setFilterRect(areaOfInterest)
for feature in layer.getFeatures(request):
    # do whatever you need with the feature

上記の例に示すように、空間フィルタの代わりに(あるいは加えて)属性ベースのフィルタが必要な場合は、QgsExpression`オブジェクトを構築し、それを:obj: QgsFeatureRequest`コンストラクタに渡すことができます。ここに例があります

# The expression will filter the features where the field "location_name" contains
# the word "Lake" (case insensitive)
exp = QgsExpression('location_name ILIKE \'%Lake%\'')
request = QgsFeatureRequest(exp)

:class:`QgsExpression`によってサポートされる構文の詳細については、:ref:`expressions`を参照。

要求は、地物ごとに取得したデータを定義するために使用できるので、反復子はすべての地物を返しますが、それぞれの地物については部分的データを返します。

# Only return selected fields
request.setSubsetOfAttributes([0,2])
# More user friendly version
request.setSubsetOfAttributes(['name','id'],layer.pendingFields())
# Don't return geometry objects
request.setFlags(QgsFeatureRequest.NoGeometry)

ちなみに

属性の部分集合だけ必要な場合、または幾何情報を必要としない場合は、上の例で示されるように「QgsFeatureRequest.NoGeometry」フラグを使用するか属性の部分集合(空も可能)を指定することによって、地物要求の**速度**をかなり向上できます。

ベクターレイヤの修正

ベクトルデータプロバイダはほとんどレイヤーデータの編集をサポートします。時には彼らは、可能な編集操作の一部分だけをサポートしています。機能のどの部分がサポートされるかを見つけるために :func:`capabilities`関数を使用します

caps = layer.dataProvider().capabilities()
# Check if a particular capability is supported:
caps & QgsVectorDataProvider.DeleteFeatures
# Print 2 if DeleteFeatures is supported

利用可能なすべての機能のリストについては、`QgsVectorDataProviderのAPIドキュメント<http://qgis.org/api/classQgsVectorDataProvider.html> ` _を参照してください

カンマ区切りリストでレイヤーの機能の説明テキストを印刷するには、次の例のように capabilitiesString() が使用できます:

caps_string = layer.dataProvider().capabilitiesString()
# Print:
# u'Add Features, Delete Features, Change Attribute Values,
# Add Attributes, Delete Attributes, Create Spatial Index,
# Fast Access to Features at ID, Change Geometries,
# Simplify Geometries with topological validation'

ベクターレイヤを編集するための以下の方法のいずれかを使用することで、変更は直接基礎となるデータストア(ファイル、データベースなど)にコミットされます。一時的な変更をしたいだけの場合は、どうするかを説明している次のセクション:ref:`編集バッファーで修正する`に跳んでください。

ノート

QGISの内部(コンソールまたはプラグインからのいずれか)で作業している場合、ジオメトリ、スタイル、属性に行われた変更を確認するため、地図キャンバスを強制的に再描画することが必要になることもあるでしょう:

# If caching is enabled, a simple canvas refresh might not be sufficient
# to trigger a redraw and you must clear the cached image for the layer
if iface.mapCanvas().isCachingEnabled():
    layer.setCacheImage(None)
else:
    iface.mapCanvas().refresh()

フィーチャの追加

いくつか:class: `QgsFeature`インスタンスを作成し、プロバイダの:func:`addFeatures`メソッドにそれらのリストを渡します。これは、2つの値を返します。結果(真/偽)および追加された地物のリスト(それらのIDはデータストアによって設定されます)。

属性を設定するには、:class: QgsFields`インスタンスを渡して地物を初期化することも、追加したいフィールドの数を渡して:func:initAttributes`を呼び出すこともできます。

if caps & QgsVectorDataProvider.AddFeatures:
    feat = QgsFeature(layer.pendingFields())
    feat.setAttributes([0, 'hello'])
    # Or set a single attribute by key or by index:
    feat.setAttribute('name', 'hello')
    feat.setAttribute(0, 'hello')
    feat.setGeometry(QgsGeometry.fromPoint(QgsPoint(123, 456)))
    (res, outFeats) = layer.dataProvider().addFeatures([feat])

フィーチャの削除

フィーチャを削除するには、フィーチャのIDの配列を渡すだけです:

if caps & QgsVectorDataProvider.DeleteFeatures:
    res = layer.dataProvider().deleteFeatures([5, 10])

フィーチャの修正

フィーチャのジオメトリの変更も属性の変更もどちらも可能です。次のサンプルは最初にインデックス 0 と 1 の属性の値を変更し、その後にフィーチャのジオメトリを変更しています

fid = 100   # ID of the feature we will modify

if caps & QgsVectorDataProvider.ChangeAttributeValues:
    attrs = { 0 : "hello", 1 : 123 }
    layer.dataProvider().changeAttributeValues({ fid : attrs })

if caps & QgsVectorDataProvider.ChangeGeometries:
    geom = QgsGeometry.fromPoint(QgsPoint(111,222))
    layer.dataProvider().changeGeometryValues({ fid : geom })

ちなみに

ジオメトリを変更する必要があるだけの場合は、:class:`QgsVectorLayerEditUtils`を使用することを考えてもよいでしょう。これはジオメトリを編集するための有用なメソッドをいくつか提供します(変換、移動、頂点挿入など)。

フィールドの追加または削除

フィールド(属性)を追加するには、フィールドの定義の配列を指定する必要があります。フィールドを削除するにはフィールドのインデックスの配列を渡すだけです

if caps & QgsVectorDataProvider.AddAttributes:
    res = layer.dataProvider().addAttributes([QgsField("mytext", QVariant.String), QgsField("myint", QVariant.Int)])

if caps & QgsVectorDataProvider.DeleteAttributes:
    res = layer.dataProvider().deleteAttributes([0])

データプロバイダのフィールドを追加または削除した後、レイヤのフィールドは、変更が自動的に反映されていないため、更新する必要があります。

layer.updateFields()

ベクターレイヤを編集バッファで修正する.

QGISアプリケーションでベクターを編集するには、個々のレイヤを編集モードにしてから編集を行って最後に変更をコミット(もしくはロールバック)します。全ての変更はそれらをコミットするまでは書き込まれません — これらはメモリ上の編集バッファに居続けます。これらの機能はプログラムで扱うことができます — これはデータプロバイダを直接使う方法を補完するベクターレイヤを編集する別の方法です。ベクターレイヤの編集機能をもったGUIツールを提供する際にこのオプションを使えば、ユーザにコミット/ロールバックをするのを決めさせられ、またundo/redoのような使い方をさせることができます。変更をコミットする時に、編集バッファの全ての変更はデータプロバイダに保存されます。

レイヤーが編集モードであるかどうかを調べるには、:func:`isEditable`を使用します—編集機能は、編集モードがオンになっている場合にのみ動作します。編集機能の使用方法

# add two features (QgsFeature instances)
layer.addFeatures([feat1,feat2])
# delete a feature with specified ID
layer.deleteFeature(fid)

# set new geometry (QgsGeometry instance) for a feature
layer.changeGeometry(fid, geometry)
# update an attribute with given field index (int) to given value (QVariant)
layer.changeAttributeValue(fid, fieldIndex, value)

# add new field
layer.addAttribute(QgsField("mytext", QVariant.String))
# remove a field
layer.deleteAttribute(fieldIndex)

適切に undo/redo が動くようにするには、上記で言及しているコマンドを undo コマンドでラップする必要があります。(もし undo/redo を気にしないで、逐一変更を保存するのであれば、 データプロバイダでの編集 で簡単に実現できるでしょう。) undo機能はこのように使います

layer.beginEditCommand("Feature triangulation")

# ... call layer's editing methods ...

if problem_occurred:
  layer.destroyEditCommand()
  return

# ... more editing ...

layer.endEditCommand()

beginEndCommand() は内部的に “アクティブな” コマンドを作成して、この後に続くベクターレイヤの変更を記録し続けます。 endEditCommand() を呼び出すことでundoスタックにコマンドがプッシュされ、ユーザがGUIからコマンドの undo/redo が可能になります。変更をしている途中でなにか問題が発生した場合は、 destroyEditCommand() メソッドでコマンドを削除してコマンドがアクティブであった時に行った全ての変更をロールバックするでしょう。

編集モードを開始するには、startEditing()`メソッドがあり、編集を中止するには:func:`CommitChanges()`と:func:`ROLLBACK() があります—しかし、通常はこれらのメソッドを必要はありませんし、この機能はユーザによってトリガーされるように残します。

次の例に示すように、よりセマンティックなコードブロックにコミットとロールバックをラップする `` - :code:`with edit(layer)`文も使用できます:

with edit(layer):
  f = layer.getFeatures().next()
  f[0] = 5
  layer.updateFeature(f)

これは最後には:func:`CommitChanges()`を自動的に呼び出します。いずれかの例外が発生した場合、それはすべての変更を:func:`rollBack()`します。:func:`CommitChanges()`中で問題に遭遇した場合(メソッドがfalseを返すとき)、:class:`QgsEditError`例外が発生します。

空間インデックスを使う

空間インデックスは、頻繁にベクターレイヤーに問い合わせをする必要がある場合、コードのパフォーマンスを劇的に改善します。例えば、補間アルゴリズムを書いていて、補間値の計算に使用するために与えられた位置に対して最も近い10点をポイントレイヤーから求める必要がある、と想像してください。空間インデックスが無いと、QGISがこれらの10点を求める方法は、すべての点から指定の場所への距離を計算してそれらの距離を比較することしかありません。これは、いくつかの場所について繰り返す必要がある場合は特に、非常に時間のかかる処理となります。もし空間インデックスがレイヤに作成されていれば、処理はもっと効率的になります。

空間インデックスの無いレイヤは、電話番号が順番に並んでいない、もしくはインデックスの無い電話帳と思ってください。所定の人の電話番号を見つける唯一の方法は、巻頭からその番号を見つけるまで読むだけです。

空間索引は、QGISベクトルレイヤーのためにデフォルトで作成されていませんが、簡単に作成できます。しなければいけないことはこうです:

  • 空間インデックスを作成する — 以下のコードは空のインデックスを作成する

index = QgsSpatialIndex()
  • クラス:インデックスに地物を追加—インデックスは `QgsFeature`オブジェクトをとり、それを追加します

    内部データ構造に。オブジェクトは手動でも作成できますし、プロバイダの:func: ` nextFeature()`の前回の呼び出しからのものも使用できます。:

index.insertFeature(feat)
  • 空間インデックスに何かしらの値が入れられると検索ができるようになります

# returns array of feature IDs of five nearest features
nearest = index.nearestNeighbor(QgsPoint(25.4, 12.7), 5)

# returns array of IDs of features which intersect the rectangle
intersect = index.intersects(QgsRectangle(22.5, 15.3, 23.1, 17.2))

ベクターレイヤの作成

QgsVectorFileWriter クラスを使ってベクターレイヤファイルを書き出す事ができます。これはOGRがサポートするいかなるベクターファイル(shapefiles, GeoJSON, KML そしてその他)をサポートしています。

ベクターレイヤをエクスポートする方法は二つあります:

  • QgsVectorLayer インスタンスから

  error = QgsVectorFileWriter.writeAsVectorFormat(layer, "my_shapes.shp", "CP1250", None, "ESRI Shapefile")

  if error == QgsVectorFileWriter.NoError:
      print "success!"

  error = QgsVectorFileWriter.writeAsVectorFormat(layer, "my_json.json", "utf-8", None, "GeoJSON")
  if error == QgsVectorFileWriter.NoError:
      print "success again!"

The third parameter specifies output text encoding. Only some drivers need this
for correct operation - shapefiles are one of those --- however in case you
are not using international characters you do not have to care much about
the encoding. The fourth parameter that we left as ``None`` may specify
destination CRS --- if a valid instance of :class:`QgsCoordinateReferenceSystem`
is passed, the layer is transformed to that CRS.

For valid driver names please consult the `supported formats by OGR`_ --- you
should pass the value in the "Code" column as the driver name. Optionally
you can set whether to export only selected features, pass further
driver-specific options for creation or tell the writer not to create
attributes --- look into the documentation for full syntax.
  • フィーチャから直接

# define fields for feature attributes. A QgsFields object is needed
fields = QgsFields()
fields.append(QgsField("first", QVariant.Int))
fields.append(QgsField("second", QVariant.String))

# create an instance of vector file writer, which will create the vector file.
# Arguments:
# 1. path to new file (will fail if exists already)
# 2. encoding of the attributes
# 3. field map
# 4. geometry type - from WKBTYPE enum
# 5. layer's spatial reference (instance of
#    QgsCoordinateReferenceSystem) - optional
# 6. driver name for the output file
writer = QgsVectorFileWriter("my_shapes.shp", "CP1250", fields, QGis.WKBPoint, None, "ESRI Shapefile")

if writer.hasError() != QgsVectorFileWriter.NoError:
    print "Error when creating shapefile: ",  w.errorMessage()

# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10,10)))
fet.setAttributes([1, "text"])
writer.addFeature(fet)

# delete the writer to flush features to disk
del writer

メモリープロバイダー

メモリープロバイダーはプラグインやサードパーティアプリケーション開発者に主に使われるでしょう。これはディスクにデータを保存せず、開発者がテンポラリなレイヤーの高速なバックエンドとして使えるようになります。

プロバイダは文字列と int と double をサポートします。

メモリープロバイダーは空間インデックスもサポートしていて、プロバイダーの createSpatialIndex() を呼ぶことで有効になります。一度空間インデックスを作成したら小さい領域内でフィーチャのiterateが高速にできるようになります(これ以降は全てのフィーチャを順にたどる必要がなくなり、指定した短形内で収まります)。

メモリープロバイダーは QgsVectorLayer のコンストラクタに "memory" をプロバイダーの文字列として与えると作成されます。

コンストラクタはレイヤーのジオメトリの種類に指定したURLを与えることができます。この種類は次のものです: "Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon" .

URIではメモリープロバイダーの座標参照系、属性フィールド、インデックスを指定することが出来ます。構文は、

crs=definition

座標参照系を指定し、この定義は QgsCoordinateReferenceSystem.createFromString() で受け付ける事ができるどんな値でも置くことができます。

index=yes

プロバイダーが空間インデックスを使うことを指定します。

field=name:type(length,precision)

レイヤーの属性を指定します。属性は名前を持ち、オプションとして種類(integer, double, string)、長さと正確性を持ちます。複数のフィールドの定義を置くことになるでしょう。

次のサンプルは全てのこれらのオプションを含んだURLです:

"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"

次のサンプルコードはメモリープロバイダーを作成してデータ投入をしている様子です:

# create layer
vl = QgsVectorLayer("Point", "temporary_points", "memory")
pr = vl.dataProvider()

# add fields
pr.addAttributes([QgsField("name", QVariant.String),
                    QgsField("age",  QVariant.Int),
                    QgsField("size", QVariant.Double)])
vl.updateFields() # tell the vector layer to fetch changes from the provider

# add a feature
fet = QgsFeature()
fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(10,10)))
fet.setAttributes(["Johny", 2, 0.3])
pr.addFeatures([fet])

# update layer's extent when new features have been added
# because change of extent in provider is not propagated to the layer
vl.updateExtents()

最後にやったことを全て確認していきましょう:

# show some stats
print "fields:", len(pr.fields())
print "features:", pr.featureCount()
e = layer.extent()
print "extent:", e.xMiniminum(), e.yMinimum(), e.xMaximum(), e.yMaximum()

# iterate over features
f = QgsFeature()
features = vl.getFeatures()
for f in features:
    print "F:", f.id(), f.attributes(), f.geometry().asPoint()

ベクタレイヤーの外観(シンボロジ)

ベクタレイヤーがレンダリングされるとき、データの外観はレイヤーによって関連付けられた レンダラーシンボル によって決定されます。シンボルはフィーチャの仮想的な表現を描画するクラスで、レンダラーはシンボルが個々のフィーチャで使われるかを決定します。

指定したレイヤのレンダラーは下記のように得ることが出来ます

renderer = layer.rendererV2()

この参照を利用して、少しだけ探索してみましょう:

print "Type:", rendererV2.type()

次の表は QGIS コアライブラリに存在するいくつかのよく知られたレンダラーです:

タイプ

クラス

詳細

singleSymbol QgsSingleSymbolRendererV2

全てのフィーチャを同じシンボルでレンダーします

categorizedSymbol QgsCategorizedSymbolRendererV2

カテゴリごとに違うシンボルを使ってフィーチャをレンダーします

graduatedSymbol QgsGraduatedSymbolRendererV2

それぞれの範囲の値によって違うシンボルを使ってフィーチャをレンダーします

カスタムレンダラーのタイプになることもあるので、上記のタイプになるとは思い込まないでください。 QgsRendererV2Registry シングルトンを検索して現在利用可能なレンダラーを見つけることもできます。

print QgsRendererV2Registry.instance().renderersList()
# Print:
[u'singleSymbol',
u'categorizedSymbol',
u'graduatedSymbol',
u'RuleRenderer',
u'pointDisplacement',
u'invertedPolygonRenderer',
u'heatmapRenderer']

レンダラーの中身をテキストフォームにダンプすることできます — デバッグ時に役に立つでしょう:

print rendererV2.dump()

単一シンボルレンダラ

レンダリングが使っているシンボルは symbol() メソッドで取得することができ、 setSymbol() メソッドで変更することができます(C++開発者へメモ: レンダラーはシンボルのオーナーシップをとります)。

特定のベクターレイヤで使用される記号は、適切なシンボルインスタンスのインスタンスを渡しながら:func: setSymbol()`を呼び出すことによって変更できます。*ポイント* 、*ライン*、*ポリゴン*レイヤーに対するシンボルは、対応するクラス、:class:`QgsMarkerSymbolV2QgsLineSymbolV2、:class:`QgsFillSymbolV2`の:func:`createSimple`関数:を呼び出すことによって作成できます。

The dictionary passed to createSimple() sets the style properties of the symbol.

たとえば、次のコード例のように、:class:`QgsMarkerSymbolV2`のインスタンスを渡しつつ:func: `setSymbol()`を呼び出すことで、特定の**ポイント**レイヤーで使用されるシンボルを置換できます:

symbol = QgsMarkerSymbolV2.createSimple({'name': 'square', 'color': 'red'})
layer.rendererV2().setSymbol(symbol)

`` name``は、マーカーの形状を示しており、以下のいずれかとすることができます。

  • 円形

  • 方形

  • 十字

  • 長方形

  • 菱形

  • 五角形

  • 三角形

  • 正三角形

  • 星形

  • regular_star
  • 矢印

  • 塗りつぶし矢印

  • ×印

シンボルインスタンスの最初のシンボルレイヤのプロパティの完全なリストを取得するには、サンプルコードに従うことができます:

print layer.rendererV2().symbol().symbolLayers()[0].properties()
# Prints
{u'angle': u'0',
u'color': u'0,128,0,255',
u'horizontal_anchor_point': u'1',
u'name': u'circle',
u'offset': u'0,0',
u'offset_map_unit_scale': u'0,0',
u'offset_unit': u'MM',
u'outline_color': u'0,0,0,255',
u'outline_style': u'solid',
u'outline_width': u'0',
u'outline_width_map_unit_scale': u'0,0',
u'outline_width_unit': u'MM',
u'scale_method': u'area',
u'size': u'2',
u'size_map_unit_scale': u'0,0',
u'size_unit': u'MM',
u'vertical_anchor_point': u'1'}

いくつかのプロパティを変更したい場合に便利です:

# You can alter a single propery...
layer.rendererV2().symbol().symbolLayer(0).setName('square')
# ... but not all properties are accessible from methods,
# you can also replace the symbol completely:
props = layer.rendererV2().symbol().symbolLayer(0).properties()
props['color'] = 'yellow'
props['name'] = 'square'
layer.rendererV2().setSymbol(QgsMarkerSymbolV2.createSimple(props))

カテゴライズドシンボルレンダラ

分類するのに使われる属性名を検索したりセットしたりすることができます: classAttribute() メソッドと setClassAttribute() メソッドを使います。

カテゴリの配列を取得するには

for cat in rendererV2.categories():
    print "%s: %s :: %s" % (cat.value().toString(), cat.label(), str(cat.symbol()))

value() はカテゴリを区別にするのに使う値で、 label() はカテゴリの詳細に使われるテキストで、 symbol() メソッドは割り当てられているシンボルを返します。

レンダラはたいていオリジナルのシンボルと識別をするためにカラーランプを保持しています: sourceColorRamp() メソッドと sourceSymbol() メソッドから呼び出せます。

階調シンボルレンダラ

このレンダラは先ほど暑かったカテゴリ分けシンボルのレンダラととても似ていますが、クラスごとの一つの属性値の代わりに領域の値として動作し、そのため数字の属性のみ使うことができます。

レンダラで使われている領域の多くの情報を見つけるには

for ran in rendererV2.ranges():
    print "%f - %f: %s %s" % (
        ran.lowerValue(),
        ran.upperValue(),
        ran.label(),
        str(ran.symbol())
      )

属性名の分類を調べるために classAttribute() をまた使うことができ、 sourceSymbol() メソッドと sourceColorRamp() メソッドも使うことができます。さらに作成された領域の測定する mode() メソッドもあります: 等間隔や変位値、その他のメソッドと一緒に使います。

もし連続値シンボルレンダラを作ろうとしているのであれば次のスニペットの例で書かれているようにします(これはシンプルな二つのクラスを作成するものを取り上げています):

from qgis.core import *

myVectorLayer = QgsVectorLayer(myVectorPath, myName, 'ogr')
myTargetField = 'target_field'
myRangeList = []
myOpacity = 1
# Make our first symbol and range...
myMin = 0.0
myMax = 50.0
myLabel = 'Group 1'
myColour = QtGui.QColor('#ffee00')
mySymbol1 = QgsSymbolV2.defaultSymbol(myVectorLayer.geometryType())
mySymbol1.setColor(myColour)
mySymbol1.setAlpha(myOpacity)
myRange1 = QgsRendererRangeV2(myMin, myMax, mySymbol1, myLabel)
myRangeList.append(myRange1)
#now make another symbol and range...
myMin = 50.1
myMax = 100
myLabel = 'Group 2'
myColour = QtGui.QColor('#00eeff')
mySymbol2 = QgsSymbolV2.defaultSymbol(
     myVectorLayer.geometryType())
mySymbol2.setColor(myColour)
mySymbol2.setAlpha(myOpacity)
myRange2 = QgsRendererRangeV2(myMin, myMax, mySymbol2 myLabel)
myRangeList.append(myRange2)
myRenderer = QgsGraduatedSymbolRendererV2('', myRangeList)
myRenderer.setMode(QgsGraduatedSymbolRendererV2.EqualInterval)
myRenderer.setClassAttribute(myTargetField)

myVectorLayer.setRendererV2(myRenderer)
QgsMapLayerRegistry.instance().addMapLayer(myVectorLayer)

シンボルの操作

シンボルを表現するには、 QgsSymbolV2 ベースクラス由来の三つの派生クラスを使います:

  • QgsMarkerSymbolV2 - ポイントのフィーチャ用

  • QgsLineSymbolV2 - ラインのフィーチャ用

  • QgsFillSymbolV2 - ポリゴンのフィーチャ用

全てのシンボルは一つ以上のシンボルレイヤーから構成されます (QgsSymbolLayerV2 の派生クラスです)。シンボルレイヤーは実際にレンダリングをして、シンボルクラス自信はシンボルレイヤのコンテナを提供するだけです。

(例えばレンダラから)シンボルのインスタンスを持っていればそれの中身を調べる事ができます: type() メソッドはそれ自身がマーカか、ラインか、シンボルで満たさたものかを返します。 dump() メソッドはシンボルの簡単な説明を返します。シンボルレイヤーの配列を取得するにはこのようにします:

for i in xrange(symbol.symbolLayerCount()):
    lyr = symbol.symbolLayer(i)
    print "%d: %s" % (i, lyr.layerType())

シンボルが使っている色を得るには color() メソッドを使い、 setColor() でシンボルの色を変えます。マーカーシンボルは他にもシンボルのサイズと回転角をそれぞれ size() メソッドと angle() メソッドで取得することができ、ラインシンボルは width() メソッドでラインの幅を返します。

サイズと幅は標準でミリメートルが使われ、角度は 度 が使われます。

シンボルレイヤの操作

前に述べたようにシンボルレイヤ(QgsSymbolLayerV2 のサブクラスです) はフィーチャの外観を決定します。一般的に使われるいくつかの基本となるシンボルレイヤのクラスがあります。これは新しいシンボルレイヤの種類を実装を可能とし、それによってフィーチャがどのようにレンダーされるかを任意にカスタマイズできます。 layerType() メソッドはシンボルレイヤクラスの一意に識別します — 基本クラスは標準で SimpleMarker 、 SimpleLine 、 SimpleFill がシンボルレイヤのタイプとなります。

次のようにシンボルレイヤクラスを与えてシンプルレイヤを作成して、シンボルレイヤのタイプの完全なリストを取得することができます。

from qgis.core import QgsSymbolLayerV2Registry
myRegistry = QgsSymbolLayerV2Registry.instance()
myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
for item in myRegistry.symbolLayersForType(QgsSymbolV2.Marker):
    print item

出力

EllipseMarker
FontMarker
SimpleMarker
SvgMarker
VectorField

QgsSymbolLayerV2Registry クラスは利用可能な全てのシンボルレイヤタイプのデータベースを管理しています。

シンボルレイヤのデータにアクセスするには、 properties() メソッドを使い、これは表現方法を決定しているプロパティの辞書のキー値を返します。それぞれのシンボルレイヤタイプはそれが使っている特定のプロパティの集合を持っています。さらに、共通して使えるメソッドとして color(), size(), angle(), width() がそれぞれセッターと対応して存在します。もちろん size と angle はマーカーシンボルレイヤだけで利用可能で、 width はラインシンボルレイヤだけで利用可能です。

カスタムシンボルレイヤタイプの作成

あなたがデータをどうレンダーするかをカスタマイズしたいと考えているとします。あなたはあなたが思うままにフィーチャを描画する独自のシンボルレイヤクラスを作ることができます。次の例は指定した半径で赤い円を描画するマーカを示しています:

class FooSymbolLayer(QgsMarkerSymbolLayerV2):

  def __init__(self, radius=4.0):
      QgsMarkerSymbolLayerV2.__init__(self)
      self.radius = radius
      self.color = QColor(255,0,0)

  def layerType(self):
     return "FooMarker"

  def properties(self):
      return { "radius" : str(self.radius) }

  def startRender(self, context):
    pass

  def stopRender(self, context):
      pass

  def renderPoint(self, point, context):
      # Rendering depends on whether the symbol is selected (QGIS >= 1.5)
      color = context.selectionColor() if context.selected() else self.color
      p = context.renderContext().painter()
      p.setPen(color)
      p.drawEllipse(point, self.radius, self.radius)

  def clone(self):
      return FooSymbolLayer(self.radius)

layerType() メソッドはシンボルレイヤーの名前を決定し、全てのシンボルレイヤーの中で一意になります。プロパティは属性の持続として使われます。 clone() メソッドは全ての全く同じ属性を含んだシンボルレイヤーのコピーを返さなくてはなりません。最後にレンダリングのメソッドについて: startRender() はフィーチャが最初にレンダリングされる前に呼び出され、 stopRender() はレンダリングが終わったら呼び出されます。そして renderPoint() メソッドでレンダリングを行います。ポイントの座標は出力対象の座標に常に変換されます。

ポリラインとポリゴンではレンダリングのメソッドが違うだけです: (ポリラインでは)それぞれのラインの配列を受け取る renderPolyline() を使います。 renderPolygon() は最初のパラメータを外輪としたポイントのリストと、2つ目のパラメータに内輪(もしくは None)のリストを受け取ります。

普通はユーザに外観をカスタマイズさせるためにシンボルレイヤータイプの属性を設定するGUIを追加すると使いやすくなります: 上記の例であればユーザは円の半径をセットできます。次のコードはwidgetの実装となります:

class FooSymbolLayerWidget(QgsSymbolLayerV2Widget):
    def __init__(self, parent=None):
        QgsSymbolLayerV2Widget.__init__(self, parent)

        self.layer = None

        # setup a simple UI
        self.label = QLabel("Radius:")
        self.spinRadius = QDoubleSpinBox()
        self.hbox = QHBoxLayout()
        self.hbox.addWidget(self.label)
        self.hbox.addWidget(self.spinRadius)
        self.setLayout(self.hbox)
        self.connect(self.spinRadius, SIGNAL("valueChanged(double)"), \
            self.radiusChanged)

    def setSymbolLayer(self, layer):
        if layer.layerType() != "FooMarker":
            return
        self.layer = layer
        self.spinRadius.setValue(layer.radius)

    def symbolLayer(self):
        return self.layer

    def radiusChanged(self, value):
        self.layer.radius = value
        self.emit(SIGNAL("changed()"))

このwidgetはシンボルプロパティのダイアログに組み込むことができます。シンボルプロパティのダイアログでシンボルレイヤータイプを選択したときにこれはシンボルレイヤーのインスタンスとシンボルレイヤー widget のインスタンスを作成します。そしてwidgetをシンボルレイヤーを割り当てるために setSymbolLayer() メソッドを呼び出します。このメソッドでwidgetがシンボルレイヤーの属性を反映するようUIを更新します。 symbolLayer() 関数はシンボルが使ってるプロパティダイアログがシンボルレイヤーを再度探すのに使われます。

いかなる属性の変更時でも、プロパティダイアログにシンボルプレビューを更新させるために widget は changed() シグナルを発生します。

私達は最後につなげるところだけまだ扱っていません: QGIS にこれらの新しいクラスを知らせる方法です。これはレジストリにシンボルレイヤーを追加すれば完了です。レジストリに追加しなくてもシンボルレイヤーを使うことはできますが、いくつかの機能が動かないでしょう: 例えばカスタムシンボルレイヤーを使ってプロジェクトファイルを読み込んだり、GUIでレイヤーの属性を編集できないなど。

私達はシンボルレイヤーのメタデータを作る必要があります

class FooSymbolLayerMetadata(QgsSymbolLayerV2AbstractMetadata):

  def __init__(self):
    QgsSymbolLayerV2AbstractMetadata.__init__(self, "FooMarker", QgsSymbolV2.Marker)

  def createSymbolLayer(self, props):
    radius = float(props[QString("radius")]) if QString("radius") in props else 4.0
    return FooSymbolLayer(radius)

  def createSymbolLayerWidget(self):
    return FooSymbolLayerWidget()

QgsSymbolLayerV2Registry.instance().addSymbolLayerType(FooSymbolLayerMetadata())

レイヤータイプ(レイヤーが返すのと同じもの)とシンボルタイプ(marker/line/fill)を親クラスのコンストラクタに渡します。 createSymbolLayer() は辞書の引数の props で指定した属性をもつシンボルレイヤーのインスタンスを作成をしてくれます。 (キー値は QString のインスタンスで、決して “str” のオブジェクトではないのに気をつけましょう) そして createSymbolLayerWidget() メソッドはこのシンボルレイヤータイプの設定 widget を返します。

最後にこのシンボルレイヤーをレジストリに追加します — これで完了です。

カスタムレンダラの作成

もしシンボルがフィーチャのレンダリングをどう行うかをカスタマイズしたいのであれば、新しいレンダラーの実装を作ると便利かもしれません。いくつかのユースケースとしてこんなことをしたいのかもしれません: フィールドの組み合わせからシンボルを決定する、現在の縮尺に合わせてシンボルのサイズを変更するなどなど。

次のコードは二つのマーカーシンボルを作成して全てのフィーチャからランダムに一つ選ぶ簡単なカスタムレンダラです

import random

class RandomRenderer(QgsFeatureRendererV2):
  def __init__(self, syms=None):
    QgsFeatureRendererV2.__init__(self, "RandomRenderer")
    self.syms = syms if syms else [QgsSymbolV2.defaultSymbol(QGis.Point), QgsSymbolV2.defaultSymbol(QGis.Point)]

  def symbolForFeature(self, feature):
    return random.choice(self.syms)

  def startRender(self, context, vlayer):
    for s in self.syms:
      s.startRender(context)

  def stopRender(self, context):
    for s in self.syms:
      s.stopRender(context)

  def usedAttributes(self):
    return []

  def clone(self):
    return RandomRenderer(self.syms)

親クラスの QgsFeatureRendererV2 のコンストラクタはレンダラの名前(レンダラの中で一意になる必要があります)が必要です。 symbolForFeature() メソッドは個々のフィーチャでどのシンボルが使われるかを一つ決定します。 startRender()stopRender() それぞれシンボルのレンダリングの初期化/終了を処理します。 usedAttributes() メソッドはレンダラが与えられるのを期待するフィールド名のリストを返すことができます。最後に clone() 関数はレンダラーのコピーを返すでしょう。

シンボルレイヤー同様、レンダラの設定をGUIからいじることができます。これは QgsRendererV2Widget の派生クラスとなります。次のサンプルコードではユーザが最初のシンボルのシンボルをセットするボタンを作成しています(訳注: サンプルを見ると色を変更しているので原文が間違っていると思われる)

class RandomRendererWidget(QgsRendererV2Widget):
  def __init__(self, layer, style, renderer):
    QgsRendererV2Widget.__init__(self, layer, style)
    if renderer is None or renderer.type() != "RandomRenderer":
      self.r = RandomRenderer()
    else:
      self.r = renderer
    # setup UI
    self.btn1 = QgsColorButtonV2()
    self.btn1.setColor(self.r.syms[0].color())
    self.vbox = QVBoxLayout()
    self.vbox.addWidget(self.btn1)
    self.setLayout(self.vbox)
    self.connect(self.btn1, SIGNAL("clicked()"), self.setColor1)

  def setColor1(self):
    color = QColorDialog.getColor(self.r.syms[0].color(), self)
    if not color.isValid(): return
    self.r.syms[0].setColor(color);
    self.btn1.setColor(self.r.syms[0].color())

  def renderer(self):
    return self.r

コンストラクタはアクティブなレイヤー(QgsVectorLayer)とグローバルなスタイル(QgsStyleV2)と現在のレンダラのインスタンスを受け取ります。もしレンダラが無かったり、レンダラが違う種類のものだったら、コンストラクタは新しいレンダラに差し替えるか、そうでなければ現在のレンダラー(必要な種類を持つでしょう)を使います。widgetの中身はレンダラーの現在の状態を表示するよう更新されます。レンダラダイアログが受け入れられたときに、現在のレンダラを取得するために widget の renderer() メソッドが呼び出されます。

最後のちょっとした作業はレンダラのメタデータとレジストリへの登録で、これらをしないとレンダラのレイヤーの読み込みは動かなく、ユーザはレンダラのリストから選択することができないでしょう。では、私達の RandomRenderer の例を終わらせましょう

class RandomRendererMetadata(QgsRendererV2AbstractMetadata):
  def __init__(self):
    QgsRendererV2AbstractMetadata.__init__(self, "RandomRenderer", "Random renderer")

  def createRenderer(self, element):
    return RandomRenderer()
  def createRendererWidget(self, layer, style, renderer):
    return RandomRendererWidget(layer, style, renderer)

QgsRendererV2Registry.instance().addRenderer(RandomRendererMetadata())

シンボルレイヤーと同様に、abstract metadataのコンストラクタはレンダラの名前を受け取るのを期待して、この名前はユーザに見え、レンダラのアイコンの追加の名前となります。 createRenderer() メソッドには QDomElement のインスタンスを渡してレンダラの状態を DOM ツリーから復元するのに使います。 createRendererWidget() メソッドは設定のwidgetを作成します。これは必ず存在する必要はなく、もしレンダラがGUIからいじらないのであれば None を返すことができます。

レンダラにアイコンを関連付けるには QgsRendererV2AbstractMetadata のコンストラクタの三番目の引数(オプション)に指定することができます — RandomRendererMetadata の __init__() 関数の中の基本クラスのコンストラクタはこうなります

QgsRendererV2AbstractMetadata.__init__(self,
       "RandomRenderer",
       "Random renderer",
       QIcon(QPixmap("RandomRendererIcon.png", "png")))

アイコンはあとからメタデータクラスの setIcon() を使って関連付けることもできます。アイコンはファイルから読み込むこと(上記を参考)も Qt のリソース から読み込むこともできます(PyQt4 はパイソン向けの .qrc コンパイラを含んでいます)。

より詳しいトピック

TODO:

QgsVectorColorRampV2)色のランプでの作業(:class:` QgsStyleV2`)スタイルで作業シンボルを作成/変更ルールベースのレンダラは( `このブログ投稿<http://snorf.net/blog/2014/03/04/symbology-of-vector-layers-in-qgis-python-plugins>`_を参照してください)探索シンボルレイヤーとレンダラ・レジストリ