QGISでのボロノイポリゴン作成で穴/制約を考慮していますか?


12

QGISでボロノイポリゴンを作成して、一般的な領域の「穴」を考慮しようとしています。例は次のとおりです。

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

GRASSコマンドでQGISを使用し、「差分」ツールを使用して穴を作成して、この画像に実際にボロノアを作成しました。穴の範囲を含む別のポリゴンシェープファイルが「差分」レイヤーとして使用されました。サンプルアプリケーションは、分析から除外する必要がある構造間で収集されたサンプリングポイントの周囲にポリゴンを作成します。

ここで2つの問題が発生します。

  1. 「差分」機能は、「穴」に伸びる一部のポリゴン境界で、100%正しく機能していないようです。これは、ポリゴンID番号(または「0」のID)を持たない属性テーブルの行を見つけることで修正できます。

  2. このタイプの事後の「穴あけ」は、画像の赤い矢印で示されているように、不連続なポリゴンになる可能性があります。

私の質問は次のとおりです。ドメインの中心にある「穴」の存在をワンステッププロセスとして考慮し、不連続ポリゴンの生成を排除できるVoronoiツールまたはプラグインはありますか?このようなツールは、他の境界が最初に「穴」の境界にぶつからない限り、ポリゴンの境界を別の境界と最も近い交差点まで延長することを想定しています。


これは、ArcGISで環境マスクを使用する(と思う)と似ていますが、反対です。これにより、作成したポリゴンを特定の境界内に制限できます。ただし、複雑な境界/穴を使用するツールを認識していません(ArcGISではマスクがそれほど複雑になる可能性があります-テストしていないので、時間があれば後で試してみるかもしれません)。
クリスW

ArcGISの理論をテストしましたが、動作しません。リンクされた質問ごとに、結果を外形に制限できます。ただし、シェイプにカットされた穴は、結果のポリゴンでは無視されます。さらに、その穴にもいくつかのポイントがある場合、ツールはエラーになり、実行に失敗します。最初の問題の違いを説明することはできませんが、2番目のスライバーの結果はまったく予想外のものではありません。結局のところ、その領域は、たとえ穴があっても同じポイントに割り当てられます。その方法を使用してから、クリーンアップ方法でスライバーを近隣に組み込むことができます。
クリスW

2
ラスターに行くことで潜在的にこれを解決できます。ラスターマスクを使用すると、ユークリッド距離は、他のポイントから出てくるセルまたはマスクラスター(境界スラムの説明)のいずれかにヒットするまで、ポイントから出ていきます。次に、ゾーンクリーンアップを実行し、結果をベクトル化してポリゴンを取得します。
クリスW

1
v.cleanを実行してvoronoi Geometryが有効であることを確認してから、ジオメトリを確認します。最後に、差分を実行して穴を作成します。
クルーウィス16

これらの穴のボロノイとは何ですか?きれいに穴を開けたくないですか?ポリゴンレイヤーが機能しないのはなぜですか?
mdsumner 16

回答:


3

これは、ラスタを使用して可能になる場合があります。まず、ポイントと境界ポリゴンを高解像度ラスターに変換します。を使用して境界のマスクを設定しますr.mask。次に、r.grow.distanceGRASS で実行し、を使用しValue= outputます。これにより、ピクセルごとに表示され、最も近いポイントになります。これをベクトルポリゴンに変換します。スライバーポリゴンを削除するには、追加の手順が必要になる場合があります。


2

これは、ラスターで確かに可能です。

このスクリーンショットは、問題をより明確に示しています。ボロノイの部分Bは、「カラスが飛ぶように」元のボロノイ中心に近いが、これは建物の周りを歩くのに時間がかかるという事実を考慮していない。OPの質問に対する私の理解は、ボロノイがこの余分な距離を考慮して建物を歩く必要があるということです。

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

@Guillaumeからの提案が気に入っています。しかし、試してみるとr.grow.distance、マスクを尊重するのに問題がありました(以下を参照してください。波紋は建物を通過してはいけません)。

私のGrassの知識はそれほど強力ではないので、何かバカなことをしているのかもしれません。それは私のものよりもはるかに少ない仕事になるので、間違いなく、最初にその提案をチェックしてください;-)

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

ステップ1-コストサーフェスを作成する

最初のステップは、コストサーフェスを作成することです。これは一度だけ行う必要があります。

  • 編集可能なレイヤー、穴などを作成します。
  • 「unit」というフィールドを追加し、1に設定します。
  • フィールド「ユニット」を使用して、「パンチアウト」ベクターレイヤー(穴のあるレイヤー)でポリゴンツーラスターを使用します。これで、レイヤー「マスク」が作成されました。1は空きスペースで、0は構築中です。
  • ラスター計算機を使用して、これをコストサーフェスに変換します。「outdoors」を1に、「indoors」を9999に設定します。これにより、建物の移動が非常に困難になります。

    (( "mask @ 1" = 1)* 1)+(( "mask @ 1" = 0)* 9999)

コストサーフェスに少しノイズを追加することで、より「自然な」結果を得ることができます(たとえば、屋外のピクセルに対して1ではなく1から3の乱数を使用します)。

ステップ2.各ボロノイ中心の累積コストラスタを作成する

これr.cost.coordinatesで、コストサーフェスレイヤーに対して(一度に1つのボロノイセルに対して)GRASSアルゴリズムを実行できます。

開始座標には、渦中心を使用します。終了座標として、お住まいの地域の角の1つを選択します。よりスムーズな結果が得られるため、「ナイツツアー」の使用をお勧めします。

結果は、1つのボロノイ中心からの移動時間が等しいラインを示しています。バンドが建物をどのように包んでいるかに注意してください。

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

これを自動化する最善の方法はわかりません。バッチモードを処理するか、pyqgisで実行することができます。

ステップ3.ラスターをマージダウンする

これにはおそらくコードが必要です。アルゴリズムは

create a raster 'A' to match the size of your cumulative cost images
fill raster 'A' with a suitably high number e.g. 9999
create an array of the same size as the raster.
for each cumulative cost raster number 1..N
    for each cell in image
        if cell < value in raster 'A'
            set value in raster 'A' to cell value
            set corresponding cell in array to cum. cost image number
write out array as a raster

このアプローチでは、各セルが障害物を考慮して最も近いボロノイ中心で分類されたラスタを生成する必要があります。

その後、ラスタからポリゴンを使用できます。次に、Generalizeプラグインを使用して、「ステップ」効果のアーティファクトをラスターから削除できます。

ステップ2と3のあいまいさについておologiesび申し上げます。誰かがもっとエレガントな解決策に夢中になることを願っています。


1
Stevenに感謝します。GRASSラスターの回避策が機能していますが、賞金の説明で述べたように、よりエレガントなソリューションを望んでいました。
暗闇

0

注#1:実行したいくつかのテストで差分ツールがうまく機能したため、提案された問題を再現できませんでした(問題の単純なジオメトリによるか、問題が発生してからツールが改善されたためです) 1年前に質問しました)。

ただし、PyQGISで差分ツールの使用を避けるための回避策を提案します。すべては、2つの入力レイヤー間のローカル交差に基づいています(下図を参照)。

  1. ボロノイポリゴンを表すポリゴンベクトルレイヤー。
  2. 解析から除外する必要がある穴/制約を表すポリゴンベクトルレイヤー。

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

注#2差分ツールを使用したくないので、「スライバー」の作成を回避することができないため(参照)、v.cleanそれらを排除するためにツールを実行する必要がありました。さらに、@クリスWが言ったように、

[...]しかし、スライバーが発生する2番目は完全に予期しないものではありません。結局、その領域は、たとえ穴が存在していても同じポイントに割り当てられます。その方法を使用して、クリーンアップ方法でスライバーを隣人に組み込むことができます。

これらの必要な前提の後、コードを投稿します。

##Voronoi_Polygons=vector polygon
##Constraints=vector polygon
##Voronoi_Cleaned=output vector

from qgis.core import *

voronoi = processing.getObject(Voronoi_Polygons)
crs = voronoi.crs().toWkt()
ex = voronoi.extent()
extent = '%f,%f,%f,%f' % (ex.xMinimum(), ex.xMaximum(), ex.yMinimum(), ex.yMaximum())

constraints = processing.getObject(Constraints)

# Create the output layer
voronoi_mod = QgsVectorLayer('Polygon?crs='+ crs, 'voronoi' , 'memory')
prov = voronoi_mod.dataProvider()
fields = voronoi.pendingFields() # Fields from the input layer
prov.addAttributes(fields) # Add input layer fields to the outLayer
voronoi_mod.updateFields()

# Spatial index containing all the 'constraints'
index_builds = QgsSpatialIndex()
for feat in constraints.getFeatures():
    index_builds.insertFeature(feat)

final_geoms = {}
final_attrs = {}

for feat in voronoi.getFeatures():
    input_geom = feat.geometry()
    input_attrs = feat.attributes()
    final_geom = []
    multi_geom = input_geom.asPolygon()
    input_geoms = [] # edges of the input geometry
    for k in multi_geom:
        input_geoms.extend(k)
    final_geom.append(input_geoms)
    idsList = index_builds.intersects(input_geom.boundingBox())
    mid_geom = [] # edges of the holes/constraints
    if len(idsList) > 0:
        req = QgsFeatureRequest().setFilterFids(idsList)
        for ft in constraints.getFeatures(req):
            geom = ft.geometry()
            hole = []
            res = geom.intersection(input_geom)
            res_geom = res.asPolygon()
            for i in res_geom:
                hole.extend(i)
                mid_geom.append(hole)
        final_geom.extend(mid_geom)
    final_geoms[feat.id()] = final_geom
    final_attrs[feat.id()] = input_attrs

# Add the features to the output layer
outGeom = QgsFeature()
for key, value in final_geoms.iteritems():
    outGeom.setGeometry(QgsGeometry.fromPolygon(value))
    outGeom.setAttributes(final_attrs[key])
    prov.addFeatures([outGeom])

# Add 'voronoi_mod' to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(voronoi_mod)

# Run 'v.clean'
processing.runalg("grass7:v.clean",voronoi_mod, 2, 0.1, extent, -1, 0.0001, Voronoi_Cleaned, None)

# Remove 'voronoi_mod' to the Layers panel
QgsMapLayerRegistry.instance().removeMapLayer(voronoi_mod)

この結果につながります:

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

明確にするために、これはv.cleanツールを使用しない結果です。

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

@LeaningCactusによる結果との違いは、今のところ、ジオメトリが壊れておらず、エラーなしで「クリーン」できることです。


川のようにマップ全体を切断するなど、穴を長くすると、問題が発生します。近隣にスライバーを追加すると、適切な制約付きボロノイ図とは非常に異なる外観のポリゴンが作成されます。やってみた
暗闇

申し訳ありませんが、わかりません。結果にエラーが見つかりましたか?ポリゴンが質問で提案されたものと類似している場合のみ、コードをテストしました。
mgri 16

残念ながら今のところコードをテストすることはできませんが、i.stack.imgur.com / Jpfra.pngでスケッチされた穴の変更で得られた結果を表示できますか?
暗闇

制約を右側の機能まで拡張すると、これが得られます。代わりに、制約を直接移動する場合、これを取得ます。
mgri 16

図面の赤い矢印が指す小さな三角形が問題です。そこにあるべきではありませんが、結果にもあります。このアプローチは質問の問題#1を解決するように思えますが、#2は未解決のままです。
暗闇
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.