私は、arcpyを使用して、シェープファイル内のすべてのポリゴンに対して方向付けられたバッファを作成したいと思います。方向付けとは、バッファーの方向を制約する2つの角度a1とa2があることを意味します。これは以下のグラフで表されます:
何か案は?
私は、arcpyを使用して、シェープファイル内のすべてのポリゴンに対して方向付けられたバッファを作成したいと思います。方向付けとは、バッファーの方向を制約する2つの角度a1とa2があることを意味します。これは以下のグラフで表されます:
何か案は?
回答:
この回答は、質問をより大きなコンテキストに置き、フィーチャのシェープファイル表現に適用可能な効率的なアルゴリズムを説明し(ポイントの「ベクトル」または「ラインストリング」として)、そのアプリケーションの例をいくつか示し、使用または移植するための実用的なコードを示しますGIS環境。
これは形態学的拡張の例です。 完全な一般論として、膨張は領域のポイントを近傍に「広げ」ます。それらが巻き上がる点の集まりが「膨張」です。GISでのアプリケーションは数多くあります。たとえば、火の広がり、文明の動き、植物の広がりなどのモデリングです。
数学的に、そして非常に優れた(しかし有用な)一般性として、膨張はリーマン多様体(平面、球、楕円体など)の一連の点を広げます。広がりは、これらの点での接線バンドルのサブセットによって規定されます。これは、各ポイントで一連のベクトル(方向と距離)が与えられることを意味します(これを「近隣」と呼びます)。これらの各ベクトルは、その基点から始まる測地線パスを表します。基点は、これらすべてのパスの端に「広がり」ます。(画像処理で従来使用されている「拡張」のはるかに限定された定義については、Wikipediaの記事を参照してください。拡散関数は指数マップとして知られています 微分幾何学で。)
フィーチャーの「バッファリング」は、そのような拡張の最も単純な例の1つです。一定の半径(バッファー半径)のディスクが、フィーチャーの各ポイントの周囲に(少なくとも概念的には)作成されます。これらのディスクの結合がバッファです。
この質問では、広がりが特定の角度範囲内(つまり、扇形内)でのみ発生することが許可されている、少し複雑な膨張の計算を求めています。これは、かなりの曲面を含まないフィーチャ(球や楕円体の小さなフィーチャ、または平面のフィーチャなど)に対してのみ意味があります。飛行機で作業しているときは、すべてのセクターを同じ方向に向けることも重要です。(ただし、風による火の広がりをモデル化している場合は、セクターを風に合わせて配置し、そのサイズを風速によっても変化させる必要があります。これが、私が指定した拡張の一般的な定義の1つの動機です。 )(楕円体のような曲面では、すべてのセクターを「同じ」方向に向けることは一般に不可能です。)
以下の状況では、膨張は比較的簡単に計算できます。
対象物は平面にあります(つまり、対象物のマップを拡張しています。うまくいけば、マップはかなり正確です)。
膨張は一定です。フィーチャのすべてのポイントでの広がりは、同じ方向の合同な近傍内で発生します。
この共通の近所は凸状です。 凸性により、計算が大幅に簡素化および高速化されます。
この質問は、このような特殊な状況に当てはまります。原点(原点となったディスクの中心)が基点にある円形セクターによる任意のポリゴンの拡張を要求します。これらのセクターが180度を超えない場合、凸面になります。(大きいセクターは常に2つの凸セクターに半分に分割できます。2つの小さい膨張を結合すると、望ましい結果が得られます。)
ユークリッド計算を実行しているため(平面での拡散を行っています)、拡張近傍をそのポイントに変換するだけで、ポイントを拡張できます。(これを行うには、近所に起源が必要です基点に対応します。たとえば、この質問のセクターの原点は、セクターが形成される円の中心です。この起源は、たまたまセクターの境界にあります。標準のGISバッファリング操作では、近傍は中心を原点とする円です。現在、原点は円の内部にあります。原点の変更は、全体の膨張をシフトするだけなので、原点を選択することは、計算上大したことではありませんが、自然現象のモデル化の観点からは、大きな問題になる可能性があります。sector
以下のコードの関数は、原点を指定する方法を示しています。)
線分を拡張するのは難しい場合がありますが、凸状の近傍の場合、注意深く選択した平行四辺形とともに2つの端点の拡張の和集合として拡張を作成できます。(スペースの都合上、このような数学的アサーションを証明するために一時停止しませんが、洞察に満ちた演習であるため、読者が独自の証明を試みることをお勧めします。)以下は、3つのセクターを使用した図(ピンクで表示)です。それらには単位半径があり、角度はタイトルに示されています。線分自体の長さは2で、水平で、黒で示されています。
平行四辺形は、垂直方向にのみセグメントから可能な限り離れているピンク色の点を特定することによって検出されます。これにより、セグメントに平行な線に沿って2つの下部のポイントと2つの上部のポイントが得られます。4つのポイントを平行四辺形に結合する必要があります(青色で表示)。右側に、セクター自体が単なるラインセグメント(真のポリゴンではない)の場合でも、これがどのように意味があるかに注意してください。そこでは、セグメント上のすべてのポイントが、北から171度東の方向に距離の範囲で平行移動されています。これらの端点のセットは、示されている平行四辺形です。この計算の詳細は、以下のコードでbuffer
定義されている関数に表示されdilate.edges
ます。
ポリラインを拡張するには、それを形成するポイントとセグメントの拡張の和集合を形成します。の最後の2行はdilate.edges
このループを実行します。
ポリゴンを拡張するには、ポリゴンの内部とその境界の拡張を含める必要があります。(このアサーションは、膨張近傍についていくつかの仮定を行います。1つは、すべての近傍に点(0,0)が含まれていることです。これにより、ポリゴンが膨張に含まれることが保証されます。可変近傍の場合、内部の膨張も想定されます。ポリゴンのポイントは、境界ポイントの拡張を超えて拡張されません。これは、一定の近傍の場合です。)
これがどのように機能するかについて、いくつかの例を見てみましょう。最初に非アゴン(詳細を明らかにするために選択)を使用し、次に円(質問のイラストと一致するように選択)を使用します。例では引き続き同じ3つの近傍を使用しますが、半径は1/3に縮小されます。
この図では、ポリゴンの内部は灰色、ポイントの拡張(セクター)はピンク、エッジの拡張(平行四辺形)はブルーです。
「円」は実際には60ゴンだけですが、円によく似ています。
ベースフィーチャがNポイント、拡張近傍がMポイントで表される場合、このアルゴリズムはO(N M)の労力を必要とします。その後、ユニオンの頂点とエッジの混乱を単純化する必要があります。これには、O(N M log(N M))の労力が必要になる場合があります。これは、GISに依頼するものです。プログラムする必要はありません。
計算量はO用(M + N)に改善することができた凸(あなたが適切に元の2つの図形の境界を記述する頂点のリストをマージして新しい境界を周りに移動する方法を考え出すことができるため)基本機能。これには、その後のクリーニングも必要ありません。
ベースフィーチャの周りを進むにつれて拡張の近傍がサイズや方向をゆっくりと変更する場合、エッジの拡張は、その端点の拡張の和集合の凸包から密接に近似できます。2つの拡張近傍にM1およびM2ポイントがある場合、これは、Shamos&Preparata、Computational Geometryで説明されているアルゴリズムを使用したO(M1 + M2)の努力で見つけることができます。したがって、K = M1 + M2 + ... + M(N)をN個の拡張近傍の頂点の総数とすると、O(K * log(K))時間で拡張を計算できます。
単純なバッファだけが必要なのに、なぜこのような一般化に取り組みたいのでしょうか。地球上の大きなフィーチャの場合、実際には一定のサイズを持つ拡張近傍(ディスクなど)は、これらの計算が実行されるマップ上でサイズが異なる場合があります。したがって、ユークリッド幾何学のすべての利点を引き続き享受しながら、楕円体に対して正確な計算を行う方法が得られます。
サンプルはこのR
プロトタイプを使用して作成されたもので、お気に入りの言語(Python、C ++など)に簡単に移植できます。構造的には、この回答で報告されている分析と類似しているため、個別に説明する必要はありません。コメントは詳細の一部を明確にします。
(三角関数の計算は、通常のポリゴンであるサンプルフィーチャとセクターを作成するためにのみ使用されることに注意してください。膨張計算のどの部分にも三角関数は必要ありません。)
#
# Dilate the vertices of a polygon/polyline by a shape.
#
dilate.points <- function(p, q) {
# Translate a copy of `q` to each vertex of `p`, resulting in a list of polygons.
pieces <- apply(p, 1, function(x) list(t(t(q)+x)))
lapply(pieces, function(z) z[[1]]) # Convert to a list of matrices
}
#
# Dilate the edges of a polygon/polyline `p` by a shape `q`.
# `p` must have at least two rows.
#
dilate.edges <- function(p, q) {
i <- matrix(c(0,-1,1,0), 2, 2) # 90 degree rotation
e <- apply(rbind(p, p[1,]), 2, diff) # Direction vectors of the edges
# Dilate a single edge from `x` to `x+v` into a parallelogram
# bounded by parts of the dilation shape that are at extreme distances
# from the edge.
buffer <- function(x, v) {
y <- q %*% i %*% v # Signed distances orthogonal to the edge
k <- which.min(y) # Find smallest distance, then the largest *after* it
l <- (which.max(c(y[-(1:k)], y[1:k])) + k-1) %% length(y)[1] + 1
list(rbind(x+q[k,], x+v+q[k,], x+v+q[l,], x+q[l,])) # A parallelogram
}
# Apply `buffer` to every edge.
quads <- apply(cbind(p, e), 1, function(x) buffer(x[1:2], x[3:4]))
lapply(quads, function(z) z[[1]]) # Convert to a list of matrices
}
#----------------------- (This ends the dilation code.) --------------------------#
#
# Display a polygon and its point and edge dilations.
# NB: In practice we would submit the polygon, its point dilations, and edge
# dilations to the GIS to create and simplify their union, producing a single
# polygon. We keep the three parts separate here in order to illustrate how
# that polygon is constructed.
#
display <- function(p, d.points, d.edges, ...) {
# Create a plotting region covering the extent of the dilated figure.
x <- c(p[,1], unlist(lapply(c(d.points, d.edges), function(x) x[,1])))
y <- c(p[,2], unlist(lapply(c(d.points, d.edges), function(x) x[,2])))
plot(c(min(x),max(x)), c(min(y),max(y)), type="n", asp=1, xlab="x", ylab="y", ...)
# The polygon itself.
polygon(p, density=-1, col="#00000040")
# The dilated points and edges.
plot.list <- function(l, c) lapply(l, function(p)
polygon(p, density=-1, col=c, border="#00000040"))
plot.list(d.points, "#ff000020")
plot.list(d.edges, "#0000ff20")
invisible(NULL) # Doesn't return anything
}
#
# Create a sector of a circle.
# `n` is the number of vertices to use for approximating its outer arc.
#
sector <- function(radius, arg1, arg2, n=1, origin=c(0,0)) {
t(cbind(origin, radius*sapply(seq(arg1, arg2, length.out=n),
function(a) c(cos(a), sin(a)))))
}
#
# Create a polygon represented as an array of rows.
#
n.vertices <- 60 # Inscribes an `n.vertices`-gon in the unit circle.
angles <- seq(2*pi, 0, length.out=n.vertices+1)
angles <- angles[-(n.vertices+1)]
polygon.the <- cbind(cos(angles), sin(angles))
if (n.vertices==1) polygon.the <- rbind(polygon.the, polygon.the)
#
# Dilate the polygon in various ways to illustrate.
#
system.time({
radius <- 1/3
par(mfrow=c(1,3))
q <- sector(radius, pi/12, 2*pi/3, n=120)
d.points <- dilate.points(polygon.the, q)
d.edges <- dilate.edges(polygon.the, q)
display(polygon.the, d.points, d.edges, main="-30 to 75 degrees")
q <- sector(radius, pi/3, 4*pi/3, n=180)
d.points <- dilate.points(polygon.the, q)
d.edges <- dilate.edges(polygon.the, q)
display(polygon.the, d.points, d.edges, main="-150 to 30 degrees")
q <- sector(radius, -9/20*pi, -9/20*pi)
d.points <- dilate.points(polygon.the, q)
d.edges <- dilate.edges(polygon.the, q)
display(polygon.the, d.points, d.edges, main="171 degrees")
})
この例(前の図から)の計算時間は、N = 60およびM = 121(左)、M = 181(中央)、およびM = 2(右)で、4分の1秒でした。ただし、そのほとんどは展示用でした。通常、このR
コードは1秒あたり約N M = 150万を処理します(表示されているすべての計算例を実行するには、わずか0.002秒かかります)。それにもかかわらず、製品M Nの外観は、詳細な近傍を介した多くの図または複雑な図の膨張を意味し、かなりの時間がかかる可能性があるため、注意してください。大きな問題に取り組む前に、小さな問題のタイミングをベンチマークします。そのような状況では、ラスターベースのソリューションを検討するかもしれません(これは実装がはるかに簡単であり、基本的に単一の近傍計算のみを必要とします)。
これはかなり広いですが、次のことができます: