PyQGIS 入れ子のループが思うように回らない

371 views
Skip to first unread message

アンファミ

unread,
Jan 6, 2017, 12:22:26 AM1/6/17
to QGIS初心者質問グループ
お世話になります。
Python超初心者で、見様見真似で書いています。

【求める動作】
iと地物ID (0-9)を昇順に総当りで組み合わせ、両者が一致したら次のiへ
【求める成果】
0 , 0
1 , 0
1 , 1
2 , 0
2 , 1
2 , 2
3 , 0
3 , 1
...
9 , 8
9 , 9
【成功例】
layer=iface.activeLayer()
features = layer.getFeatures()

for i in range(layer.featureCount()):
  for j in range(layer.featureCount()):
    print str(i) + " , " + str(j)
    if i==j:
      break

【失敗例】
layer=iface.activeLayer()
features = layer.getFeatures()

for i in range(layer.featureCount()):
  for feature in features:
    print str(i) + " , " + str(feature.id())
    if i==feature.id():
      break

【失敗例の成果】
0 , 0
1 , 1
2 , 2
3 , 3
...
9 , 9
【失敗例の問題点】
iとfeature.idループが連動しているかのよう?に見えます。
featuresのループを完結させてから次のiに進ませたいです。

------
最初に失敗例を書き、思うように動かないので地物IDをjで指定したところ(成功例)
思うように動くようになりました。
for feature in features: で、地物ID順に全地物にアクセスするループだと思ったのですが、
成功例とどう違うのか分かりません。

↓↓は、0-9の数字を順に出力(想定通りの動作)します。
layer=iface.activeLayer()
features = layer.getFeatures()

for feature in features:
  print feature.id()


失敗例の何が悪いか、for feature in features: を使う場合はどう書いたら良いかなど、
ご存知の方ご教授ください。

アンファミ

unread,
Jan 6, 2017, 12:31:31 AM1/6/17
to QGIS初心者質問グループ
失敗例追加
breakしなければどうなるのだろうと試したところ、i==0のままで終わってしまいました。
全くもって動作基準が分かりません…

【失敗例2】
layer=iface.activeLayer()
features = layer.getFeatures()

for i in range(layer.featureCount()):
  for feature in features:
    print str(i) + " , " + str(feature.id())

【失敗例2成果】
0 , 0
0 , 1
0 , 2
0 , 3
0 , 4
0 , 5
0 , 6
0 , 7
0 , 8
0 , 9

tonaok...@gmail.com

unread,
Jan 7, 2017, 11:05:29 PM1/7/17
to QGIS初心者質問グループ
QgsVectorLayer::getFeatures で取得できるものは、配列ではなく QgsFeatureIterator クラスのオブジェクトです。
名前のとおりイテレータ(繰返し処理用のクラス)でして、「現在の位置の情報(インデックス)を含んだ配列」のようなものです。


for feature in features では、「現在位置から見て次の地物」を繰返し取得する処理になっていますので、
for j in range(layer.featureCount()) と異なり、前の処理の続きからの繰返しとなります。

(例)
features = iface.activeLayer().getFeatures()

print u"1回目"
for feature in features:
   print feature.id()
   if feature.id() == 3:
      break

print u"2回目"
for feature in features:
   print feature.id()

print u"3回目"
for feature in features:
   print feature.id()

2回目では 4 から。3回目では(末端まで到達しているので)何も出力されません。



> for feature in features: を使う場合はどう書いたら良いか
内側のループが始まる前に、 features のインデックスを 0 に戻す必要がありますので、

# QgsFeatureIterator を生成しなおす
layer=iface.activeLayer()

for i in range(layer.featureCount()):
   features = layer.getFeatures()
   for feature in features:
      print str(i) + " , " + str(feature.id())
      if i==feature.id():
         break


# あるいは現在位置を巻き戻す処理( rewind )を行う
layer=iface.activeLayer()
features = layer.getFeatures()

for i in range(layer.featureCount()):
   features.rewind()
   for feature in features:
      print str(i) + " , " + str(feature.id())
      if i==feature.id():
         break



上記を試してみてください。

ただし、そもそもとして地物の削除等で、必ずしも id が0から連番となっている保証はないため、意図した処理とならない
場合があると思います。そうすると、成功例のように i と j でインデックスで比較するか、あるいは、一旦、地物 ID の配列を
作成し、それを外側のループで用い、 id で比較するか。どちらかにしたほうがいいと思います。

アンファミ

unread,
Jan 9, 2017, 8:04:52 PM1/9/17
to QGIS初心者質問グループ
遅くなりまして申し訳ありません。大変分かりやすい解説ありがとうございます。

> 「現在位置から見て次の地物」を繰返し取得する処理
これで謎が解決しました。確かにその通りの動作ですね。
VBAでいうfor eachに当たるようなループ処理だと思っていました。

地物の削除・切り貼り等でIDが0からの連番でなくなることも、懸念事項の1つではありました。
> 一旦、地物 ID の配列を作成し、それを外側のループで用い、 id で比較する
今後はこの方法を使うようにします。

本当にありがとうございました。
Reply all
Reply to author
Forward
0 new messages