湖などのポリゴンの東西方向の最大幅に興味があります。境界ボックスは、単純なポリゴンでのみ役立ちますが、複雑な凹面ポリゴンでは役立ちません。
湖などのポリゴンの東西方向の最大幅に興味があります。境界ボックスは、単純なポリゴンでのみ役立ちますが、複雑な凹面ポリゴンでは役立ちません。
回答:
これには、GISプラットフォームでのスクリプト作成が必要になる可能性があります。
最も効率的な方法(漸近的に)は垂直ラインスイープです:最小y座標でエッジをソートし、O(e * log( e))eエッジが含まれる場合のアルゴリズム。
手順は単純ではありますが、すべての場合に正しく実行するには驚くほどトリッキーです。 ポリゴンは厄介なことがあります:ダングル、スライバー、穴、切断、頂点の複製、直線に沿った頂点の実行、隣接する2つのコンポーネント間の未解決の境界があります。これらの特性の多く(およびそれ以上)を示す例を次に示します。
具体的には、ポリゴンの閉包内に完全に収まる最大長の水平セグメントを探します。 たとえば、これにより、x = 10とx = 25の間の穴から発散するx = 20とx = 40の間のダングルがなくなります。次に、最大長の水平セグメントの少なくとも1つが少なくとも1つの頂点と交差することを示すのは簡単です。(頂点と交差しないソリューションがある場合、それらは少なくとも1つの頂点と交差するソリューションによって上下を境界とする平行四辺形の内部にあります。これにより、すべてのソリューションを見つけることができます。)
したがって、ラインスイープは最も低い頂点から開始し、上方向に(つまり、より高いy値に向かって)各頂点で停止する必要があります。各停留所で、その標高から上方に向かって広がる新しいエッジを見つけます。その高さで下から終了するエッジを削除します(これは重要なアイデアの1つです。アルゴリズムを簡素化し、潜在的な処理の半分を削除します)。完全に一定の高さにあるすべてのエッジ(水平エッジ)を慎重に処理します。
たとえば、y = 10のレベルに達したときの状態を考えます。左から右に、次のエッジがあります。
x.min x.max y.min y.max
[1,] 10 0 0 30
[2,] 10 24 10 20
[3,] 20 24 10 20
[4,] 20 40 10 10
[5,] 40 20 10 10
[6,] 60 0 5 30
[7,] 60 60 5 30
[8,] 60 70 5 20
[9,] 60 70 5 15
[10,] 90 100 10 40
この表では、(x.min、y.min)はエッジの下端の座標であり、(x.max、y.max)は上端の座標です。このレベル(y = 10)では、最初のエッジはその内部でインターセプトされ、2番目のエッジはその下部でインターセプトされます。(10,0)から(10,10)など、このレベルで終了するエッジの一部はリストに含まれません。
内側のポイントと外側のポイントの位置を判断するには、左端(もちろん、ポリゴンの外側)から開始し、水平に右に移動することを想像してください。水平ではないエッジを横切るたびに、外側から内側、そして後ろに交互に切り替わります。(これは別の重要なアイデアです。)ただし、水平エッジ内のすべてのポイントは、何であれポリゴンの内側にあると判断されます。(ポリゴンの閉包には、常にエッジが含まれます。)
例の続きとして、非水平エッジがy = 10の線で始まる、または交差するx座標のソートされたリストがあります。
x.array 6.7 10 20 48 60 63.3 65 90
interior 1 0 1 0 1 0 1 0
(x = 40がこのリストにないことに注意してください。)interior
配列の値は、内部セグメントの左端をマークします。1は内部間隔、0は外部間隔を示します。したがって、最初の1は、x = 6.7からx = 10までの間隔がポリゴン内にあることを示します。次の0は、x = 10からx = 20までの間隔がポリゴンの外側にあることを示します。そのため、次のように進みます。配列は、ポリゴンの内部として4つの別々の間隔を識別します。
x = 60からx = 63.3までの間隔など、これらの間隔の一部は、頂点と交差しません。y= 10のすべての頂点のx座標に対するクイックチェックは、そのような間隔を削除します。
スキャン中に、これらの間隔の長さを監視し、これまでに見つかった最大長の間隔に関するデータを保持できます。
このアプローチの意味合いに注意してください。「v」字型の頂点は、検出されると、2つのエッジの原点になります。そのため、交差するときに2つのスイッチが発生します。これらのスイッチはキャンセルされます。逆さまの「v」は、左から右へのスキャンを開始する前に両方のエッジが除去されるため、処理されません。どちらの場合も、そのような頂点は水平セグメントをブロックしません。
3つ以上のエッジが頂点を共有できます:これは、(10,0)、(60,5)、(25、20)で示されており、(わかりにくいですが)(20,10)および(40)で示されています、10)。(これは、ダングルが(20,10)->(40,10)->(40,0)->(40、-50)->(40、10)->(20、 10)。(40,0)の頂点が別のエッジの内部にもあることに注意してください。
一番下に注意が必要な状況が示されています。非水平セグメントのx座標は
30, 50
これにより、x = 30の左側のすべてが外部、30〜50のすべてが内部、50以降がすべて外部と見なされます。このアルゴリズムでは、x = 40の頂点は考慮されません。
スキャンの終了時のポリゴンは次のとおりです。頂点を含むすべての内部間隔を濃い灰色で、最大長の間隔を赤で表示し、y座標に従って頂点に色を付けます。最大間隔は64単位です。
関係する唯一の幾何学的計算は、エッジが水平線と交差する場所を計算することです。これは単純な線形補間です。また、どの内部セグメントに頂点が含まれているかを判断するための計算も必要です。これらは、2つの不等式で簡単に計算される中間の判断です。この単純さにより、アルゴリズムは堅牢で、整数および浮動小数点の両方の座標表現に適しています。
座標が地理的な場合、水平線は実際には緯度の円上にあります。それらの長さを計算するのは難しくありません。ユークリッドの長さに緯度の余弦を掛けるだけです(球体モデルで)。したがって、このアルゴリズムは地理座標にうまく適合します。(+ -180子午線の回り込みをうまく処理するには、まず、ポリゴンを通過しない南極から北極への曲線を見つける必要があります。すべてのx座標を、それに対する水平変位として再表現した後曲線、このアルゴリズムは最大水平セグメントを正しく検出します。)
以下は、R
計算を実行し、イラストを作成するために実装されたコードです。
#
# Plotting functions.
#
points.polygon <- function(p, ...) {
points(p$v, ...)
}
plot.polygon <- function(p, ...) {
apply(p$e, 1, function(e) lines(matrix(e[c("x.min", "x.max", "y.min", "y.max")], ncol=2), ...))
}
expand <- function(bb, e=1) {
a <- matrix(c(e, 0, 0, e), ncol=2)
origin <- apply(bb, 2, mean)
delta <- origin %*% a - origin
t(apply(bb %*% a, 1, function(x) x - delta))
}
#
# Convert polygon to a better data structure.
#
# A polygon class has three attributes:
# v is an array of vertex coordinates "x" and "y" sorted by increasing y;
# e is an array of edges from (x.min, y.min) to (x.max, y.max) with y.max >= y.min, sorted by y.min;
# bb is its rectangular extent (x0,y0), (x1,y1).
#
as.polygon <- function(p) {
#
# p is a list of linestrings, each represented as a sequence of 2-vectors
# with coordinates in columns "x" and "y".
#
f <- function(p) {
g <- function(i) {
v <- p[(i-1):i, ]
v[order(v[, "y"]), ]
}
sapply(2:nrow(p), g)
}
vertices <- do.call(rbind, p)
edges <- t(do.call(cbind, lapply(p, f)))
colnames(edges) <- c("x.min", "x.max", "y.min", "y.max")
#
# Sort by y.min.
#
vertices <- vertices[order(vertices[, "y"]), ]
vertices <- vertices[!duplicated(vertices), ]
edges <- edges[order(edges[, "y.min"]), ]
# Maintaining an extent is useful.
bb <- apply(vertices <- vertices[, c("x","y")], 2, function(z) c(min(z), max(z)))
# Package the output.
l <- list(v=vertices, e=edges, bb=bb); class(l) <- "polygon"
l
}
#
# Compute the maximal horizontal interior segments of a polygon.
#
fetch.x <- function(p) {
#
# Update moves the line from the previous level to a new, higher level, changing the
# state to represent all edges originating or strictly passing through level `y`.
#
update <- function(y) {
if (y > state$level) {
state$level <<- y
#
# Remove edges below the new level from state$current.
#
current <- state$current
current <- current[current[, "y.max"] > y, ]
#
# Adjoin edges at this level.
#
i <- state$i
while (i <= nrow(p$e) && p$e[i, "y.min"] <= y) {
current <- rbind(current, p$e[i, ])
i <- i+1
}
state$i <<- i
#
# Sort the current edges by x-coordinate.
#
x.coord <- function(e, y) {
if (e["y.max"] > e["y.min"]) {
((y - e["y.min"]) * e["x.max"] + (e["y.max"] - y) * e["x.min"]) / (e["y.max"] - e["y.min"])
} else {
min(e["x.min"], e["x.max"])
}
}
if (length(current) > 0) {
x.array <- apply(current, 1, function(e) x.coord(e, y))
i.x <- order(x.array)
current <- current[i.x, ]
x.array <- x.array[i.x]
#
# Scan and mark each interval as interior or exterior.
#
status <- FALSE
interior <- numeric(length(x.array))
for (i in 1:length(x.array)) {
if (current[i, "y.max"] == y) {
interior[i] <- TRUE
} else {
status <- !status
interior[i] <- status
}
}
#
# Simplify the data structure by retaining the last value of `interior`
# within each group of common values of `x.array`.
#
interior <- sapply(split(interior, x.array), function(i) rev(i)[1])
x.array <- sapply(split(x.array, x.array), function(i) i[1])
print(y)
print(current)
print(rbind(x.array, interior))
markers <- c(1, diff(interior))
intervals <- x.array[markers != 0]
#
# Break into a list structure.
#
if (length(intervals) > 1) {
if (length(intervals) %% 2 == 1)
intervals <- intervals[-length(intervals)]
blocks <- 1:length(intervals) - 1
blocks <- blocks - (blocks %% 2)
intervals <- split(intervals, blocks)
} else {
intervals <- list()
}
} else {
intervals <- list()
}
#
# Update the state.
#
state$current <<- current
}
list(y=y, x=intervals)
} # Update()
process <- function(intervals, x, y) {
# intervals is a list of 2-vectors. Each represents the endpoints of
# an interior interval of a polygon.
# x is an array of x-coordinates of vertices.
#
# Retains only the intervals containing at least one vertex.
between <- function(i) {
1 == max(mapply(function(a,b) a && b, i[1] <= x, x <= i[2]))
}
is.good <- lapply(intervals$x, between)
list(y=y, x=intervals$x[unlist(is.good)])
#intervals
}
#
# Group the vertices by common y-coordinate.
#
vertices.x <- split(p$v[, "x"], p$v[, "y"])
vertices.y <- lapply(split(p$v[, "y"], p$v[, "y"]), max)
#
# The "state" is a collection of segments and an index into edges.
# It will updated during the vertical line sweep.
#
state <- list(level=-Inf, current=c(), i=1, x=c(), interior=c())
#
# Sweep vertically from bottom to top, processing the intersection
# as we go.
#
mapply(function(x,y) process(update(y), x, y), vertices.x, vertices.y)
}
scale <- 10
p.raw = list(scale * cbind(x=c(0:10,7,6,0), y=c(3,0,0,-1,-1,-1,0,-0.5,0.75,1,4,1.5,0.5,3)),
scale *cbind(x=c(1,1,2.4,2,4,4,4,4,2,1), y=c(0,1,2,1,1,0,-0.5,1,1,0)),
scale *cbind(x=c(6,7,6,6), y=c(.5,2,3,.5)))
#p.raw = list(cbind(x=c(0,2,1,1/2,0), y=c(0,0,2,1,0)))
#p.raw = list(cbind(x=c(0, 35, 100, 65, 0), y=c(0, 50, 100, 50, 0)))
p <- as.polygon(p.raw)
results <- fetch.x(p)
#
# Find the longest.
#
dx <- matrix(unlist(results["x", ]), nrow=2)
length.max <- max(dx[2,] - dx[1,])
#
# Draw pictures.
#
segment.plot <- function(s, length.max, colors, ...) {
lapply(s$x, function(x) {
col <- ifelse (diff(x) >= length.max, colors[1], colors[2])
lines(x, rep(s$y,2), col=col, ...)
})
}
gray <- "#f0f0f0"
grayer <- "#d0d0d0"
plot(expand(p$bb, 1.1), type="n", xlab="x", ylab="y", main="After the Scan")
sapply(1:length(p.raw), function(i) polygon(p.raw[[i]], col=c(gray, "White", grayer)[i]))
apply(results, 2, function(s) segment.plot(s, length.max, colors=c("Red", "#b8b8a8"), lwd=4))
plot(p, col="Black", lty=3)
points(p, pch=19, col=round(2 + 2*p$v[, "y"]/scale, 0))
points(p, cex=1.25)
これがラスターベースのソリューションです。 それは高速で(最初から最後まですべての作業を14分で完了しました)、スクリプトを作成する必要がなく、わずかな操作しか必要なく、かなり正確です。
ポリゴンのラスター表現から始めます。 これは、550行と1200列のグリッドを使用します。
この表現では、灰色(内側)のセルの値は1で、他のすべてのセルはNoDataです。
ウェイトグリッドの単位セル値(「降雨量」)を使用して、西から東への流れの累積を計算します。
低蓄積は暗く、明るい黄色で最高の蓄積に増加します。
ゾーンの最大値(グリッドのポリゴンと値のフロー累積を使用)は、フローが最大のセルを識別します。これらを表示するには、右下にズームインする必要がありました。
赤色のセルは、フローの最も高い累積の終わりを示します。これらは、ポリゴンの最大長の内部セグメントの右端の終点です。
これらのセグメントを見つけるには、すべての重量を赤いセルに配置し、逆方向にフローを実行します!
下部近くの赤いストライプは、2行のセルを示しています。その中には、最大長の水平セグメントがあります。この表現をそのまま使用して、さらに分析するか、ポリライン(またはポリゴン)形状に変換します。
ラスター表現で行われたいくつかの離散化エラーがあります。計算時間をいくらか犠牲にして、解像度を上げることで減らすことができます。
このアプローチの非常に素晴らしい側面の1つは、パイプラインやサッカー場の設置、エコロジカルバッファーの作成など、何らかの目標を達成する必要がある大きなワークフローの一部として、物事の極端な価値を見つけることです。このプロセスにはトレードオフが伴います。したがって、非常に長い水平線は最適なソリューションの一部ではない可能性があります。代わりに、ほとんどの最も長い行がどこにあるかを知ることに注意するかもしれません。これは簡単です。ゾーン最大フローを選択するのではなく、ゾーン最大に近いすべてのセルを選択します。この例では、ゾーンの最大値は744(最長の内部セグメントがまたがる列の数)に等しくなります。代わりに、その最大値の5%以内のすべてのセルを選択しましょう。
フローを東から西に実行すると、次の水平セグメントのコレクションが生成されます。
これは、連続する東西範囲がポリゴン内の最大東西範囲よりも95%以上大きい場所のマップです。
OK。別の(より良い)アイデア(idea-№2)があります。しかし、SQLクエリとしてではなく、Pythonスクリプトとして実現する方が良いと思います。ここでも、EWだけでなく一般的なケースがあります。
ポリゴンのバウンディングボックスと、測定方向としての方位角(A)が必要です。BBoxのエッジの長さがLAとLBであると仮定します。ポリゴン内の最大可能距離(MD)はですMB = (LA^2 * LB^2)^(1/2)
。したがって、シーク値(V)はMB:より大きくありませんV <= MB
。
Fetzerの答えがあなたのやりたいことかどうかはわかりませんが、st_box2dが仕事をするかもしれません。
SS_RebeliousのアイデアN°1は多くの場合機能しますが、一部の凹面ポリゴンでは機能しません。
東西線の可能性がある場合、頂点で作られた線が多角形の境界を横切るときに、ポイントがエッジに従う人工的なlwラインを作成する必要があると思います。
このために、線の長さが長い4ノードのポリゴンを作成し、元のポリゴンとオーバーラップする前のポリゴンP *を作成し、min(y1)およびmax(y2)がx-lineを残すかどうかを確認できます。可能性。(ここで、y1は4つのノードポリゴンの左上のコルネットと右上隅の間のポイントのセット、y2は左下と右下隅の間のyのセットです)。これはそれほど簡単ではありません。psqlツールが役立つことを願っています。
私はアイデア-№1を持っています(編集:一般的な場合、EW方向だけでなく、コメントで説明されているいくつかの制限付き)。コードを提供するのではなく、概念を提供します。「x方向」は実際には方位角であり、ST_Azimuthによって計算されます。提案される手順は次のとおりです。
湖のポリゴンに多数のポイントがあれば、これらのポイントに方位角(アスペクト、地理方向)で線を作成できます。最も離れた2つの線の間の最短線を計算できるように、十分に長い線の長さ(ST_MakePoint部分)を選択します。
以下に例を示します。
この例は、ポリゴンの最大幅を示しています。このアプローチでは、ST_ShortestLine(赤い線)を選択します。ST_MakeLineは値を増やし(青い線)、線の終点(左下)はポリゴンの青い線に当たります。作成された(ヘルプ)ラインの重心で距離を計算する必要があります。
このアプローチのための不規則または凹面のポリゴンのアイデア。ポリゴンとラスタを交差させる必要があるかもしれません。