SpatialLinesオブジェクトの類似性を測定する方法


9

SpatialLinesRで2つのオブジェクトを作成しました図

これらのオブジェクトは次の方法で作成されました:

library(sp)
xy <- cbind(x,y)
xy.sp = sp::SpatialPoints(xy)
spl1 <- sp::SpatialLines(list(Lines(Line(xy.sp), ID="a")))

ここで、これを回転して反転した同じ線であり、それらの差が0に等しい(つまり、形状が等しい)と結論付けたいと思います。

これを行うには、maptoolsパッケージを使用して行#1を回転できます。

spl180 <- maptools::elide(spl1, rotate=180)

次に、次のようにrgeos、パッケージを使用して、回転された各ラインをライン#2に対してチェックする必要があります。

hdist <- rgeos::gDistance(spl180, spl2, byid=FALSE, hausdorff=TRUE)

ただし、これはSpatialLines、特にオブジェクトの数が約1000である場合に、オブジェクトを照合するための計算コストのかかる方法です。

この仕事をする賢い方法はありますか?

PSさらに、上記のアプローチは、可能なすべての回転と反転を保証するものではありません。

P.S2。ライン#1がライン#2に対して縮小表示されている場合でも、ライン#1と#2の差は0である必要があります。

更新:

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

回答:


9

真に汎用的な効果的な方法は、形状の表現を標準化し、内部表現の回転、並進、反射、またはささいな変更によって変更されないようにします。

これを行う1つの方法は、接続された各形状を、一端から開始して、エッジの長さと(符号付き)角度の交互のシーケンスとしてリストすることです。(形状は、長さゼロのエッジまたは直線角度がないという意味で「クリーン」である必要があります。)反射の下でこれを不変にするには、最初のゼロ以外の角度が負の場合、すべての角度を無効にします。

いずれかに接続ポリラインので(n個の頂点を有することになるN -1エッジがで区切られたN -2角度は、私はそれが便利発見したR二つのアレイ、エッジの長さのための1つからなるデータ構造で使用するために、次のコード$lengthsとのための他のangles、。$anglesラインセグメントには角度がまったくないため、このようなデータ構造では長さ0の配列を処理することが重要です。

このような表現は、辞書式順序で並べることができます。 標準化プロセス中に蓄積された浮動小数点エラーをある程度考慮する必要があります。エレガントな手順では、これらのエラーを元の座標の関数として推定します。以下のソリューションでは、2つの長さが相対的に非常にわずかに異なる場合に2つの長さが等しいと見なされる、より単純な方法が使用されています。 角度の違いは、絶対的に非常に小さい場合のみです。

基礎となる方向の反転の下でそれらを不変にするには、ポリラインのそれとその反転の間の辞書式に最も早い表現を選択します。

マルチパートポリラインを処理するには、コンポーネントを辞書式順序で配置します。

ユークリッド変換の下で等価クラスを見つけるには

  • 形状の標準化された表現を作成します。

  • 標準化された表現の辞書式ソートを実行します。

  • 等しい表現のシーケンスを識別するために、ソートされた順序を通過します。

計算時間はO(n * log(n)* N)に比例します。nはフィーチャの数、Nは任意のフィーチャの頂点の最大数です。 これは効率的です。

おそらく、ポリラインの長さ、中心、その中心に関するモーメントなどの簡単に計算される不変の幾何学的プロパティに基づく予備的なグループ化は、多くの場合、プロセス全体を合理化するために適用できることに言及する価値があります。そのような各予備グループ内で合同な特徴のサブグループを見つけるだけでよい。ここで与えられた完全な方法は、他の点では非常に類似していて、そのような単純な不変式でも区別できないような形状に必要です。たとえば、ラスターデータから構築された単純なフィーチャには、そのような特性があります。ただし、ここで示すソリューションは非常に効率的であるため、それを実装するための努力をすれば、それだけで問題なく機能する可能性があります。


左側の図は、5つのポリラインと、ランダムな平行移動、回転、反射、および内部方向の反転(表示されていない)を介して取得された15個のポリラインを示しています。右側の図は、ユークリッド同値類に従ってそれらに色を付けます。共通の色のすべての図は合同です。異なる色は合同ではありません。

図

Rコードが続きます。 入力が500のシェイプ、500の追加(合同)シェイプ、シェイプごとに平均100頂点に更新された場合、このマシンでの実行時間は3秒でした。

このコードは不完全です:ので、Rネイティブの辞書の並べ替えを持っていない、と私は単純に最初の各標準化された形状の座標でソートを実行し、ゼロから1を符号化するような気がしませんでした。ここで作成されたランダムな形状についてはそれで問題ありませんが、本番環境では完全な辞書編集ソートを実装する必要があります。order.shapeこの変更の影響を受けるのは関数のみです。その入力は標準化された形状のリストでsあり、その出力はsそれをソートするためのインデックスのシーケンスです。

#
# Create random shapes.
#
n.shapes <- 5      # Unique shapes, up to congruence
n.shapes.new <- 15 # Additional congruent shapes to generate
p.mean <- 5        # Expected number of vertices per shape
set.seed(17)       # Create a reproducible starting point
shape.random <- function(n) matrix(rnorm(2*n), nrow=2, ncol=n)
shapes <- lapply(2+rpois(n.shapes, p.mean-2), shape.random)
#
# Randomly move them around.
#
move.random <- function(xy) {
  a <- runif(1, 0, 2*pi)
  reflection <- sign(runif(1, -1, 1))
  translation <- runif(2, -8, 8)
  m <- matrix(c(cos(a), sin(a), -sin(a), cos(a)), 2, 2) %*%
    matrix(c(reflection, 0, 0, 1), 2, 2)
  m <- m %*% xy + translation
  if (runif(1, -1, 0) < 0) m <- m[ ,dim(m)[2]:1]
  return (m)
}
i <- sample(length(shapes), n.shapes.new, replace=TRUE)
shapes <- c(shapes, lapply(i, function(j) move.random(shapes[[j]])))
#
# Plot the shapes.
#
range.shapes <- c(min(sapply(shapes, min)), max(sapply(shapes, max)))
palette(gray.colors(length(shapes)))
par(mfrow=c(1,2))
plot(range.shapes, range.shapes, type="n",asp=1, bty="n", xlab="", ylab="")
invisible(lapply(1:length(shapes), function(i) lines(t(shapes[[i]]), col=i, lwd=2)))
#
# Standardize the shape description.
#
standardize <- function(xy) {
  n <- dim(xy)[2]
  vectors <- xy[ ,-1, drop=FALSE] - xy[ ,-n, drop=FALSE]
  lengths <- sqrt(colSums(vectors^2))
  if (which.min(lengths - rev(lengths))*2 < n) {
    lengths <- rev(lengths)
    vectors <- vectors[, (n-1):1]
  }
  if (n > 2) {
    vectors <- vectors / rbind(lengths, lengths)
    perps <- rbind(-vectors[2, ], vectors[1, ])
    angles <- sapply(1:(n-2), function(i) {
      cosine <- sum(vectors[, i+1] * vectors[, i])
      sine <- sum(perps[, i+1] * vectors[, i])
      atan2(sine, cosine)
    })
    i <- min(which(angles != 0))
    angles <- sign(angles[i]) * angles
  } else angles <- numeric(0)
  list(lengths=lengths, angles=angles)
}
shapes.std <- lapply(shapes, standardize)
#
# Sort lexicographically.  (Not implemented: see the text.)
#
order.shape <- function(s) {
  order(sapply(s, function(s) s$lengths[1]))
}
i <- order.shape(shapes.std)
#
# Group.
#
equal.shape <- function(s.0, s.1) {
  same.length <- function(a,b) abs(a-b) <= (a+b) * 1e-8
  same.angle <- function(a,b) min(abs(a-b), abs(a-b)-2*pi) < 1e-11
  r <- function(u) {
    a <- u$angles
    if (length(a) > 0) {
      a <- rev(u$angles)
      i <- min(which(a != 0))
      a <- sign(a[i]) * a
    }
    list(lengths=rev(u$lengths), angles=a)
  }
  e <- function(u, v) {
    if (length(u$lengths) != length(v$lengths)) return (FALSE)
    all(mapply(same.length, u$lengths, v$lengths)) &&
      all(mapply(same.angle, u$angles, v$angles))
    }
  e(s.0, s.1) || e(r(s.0), s.1)
}
g <- rep(1, length(shapes.std))
for (j in 2:length(i)) {
  i.0 <- i[j-1]
  i.1 <- i[j]
  if (equal.shape(shapes.std[[i.0]], shapes.std[[i.1]])) 
    g[j] <- g[j-1] else g[j] <- g[j-1]+1
}
palette(rainbow(max(g)))
plot(range.shapes, range.shapes, type="n",asp=1, bty="n", xlab="", ylab="")
invisible(lapply(1:length(i), function(j) lines(t(shapes[[i[j]]]), col=g[j], lwd=2)))

変換のグループに任意の拡張(または「等式」)が含まれている場合、等価クラスはアフィンジオメトリの合同クラスです。この複雑化は簡単に処理できます。たとえば、すべてのポリラインを標準化して、ユニットの長さを合計します。
whuber

どうもありがとう。ただ1つの質問:形状はSpatialLinesまたはSpatialPolygonsとして表す必要がありますか?
クラウソスクラソス2015年

ポリゴンは別の複雑さを生み出します。それらの境界には明確な終点がありません。これを処理する方法はたくさんあります。たとえば、最初にxy辞書式順序で並べ替える頂点から(たとえば)頂点で始まる表現を標準化し、ポリゴンの周りを反時計回りに進めます。(トポロジ的に「クリーンな」接続されたポリゴンには、そのような頂点が1つだけ含まれます。)形状がポリゴンと見なされるかポリラインと見なされるかは、その形状が表すフィーチャの種類によって異なります。ポリラインまたはポリゴンを意図しています。
whuber

簡単な質問で申し訳ありませんが、あなたの例を理解するために私はそれを尋ねるべきです。オブジェクトshapes.stdには、$ lengthsと$ anglesの両方があります。ただし、このコードをxyデータ([1、] 3093.5 -2987.8 [2、] 3072.7 -2991.0など)で実行すると、角度が推定されず、形状も描画されません。plot(shapes [[1]])を実行すると、ポリラインがはっきり見えます。それで、私のデータでコードをテストできるようにするには、ポリラインをRに保存する方法を教えてください。
クラウソスクラウソス2015年

私はあなたがしたのと同じデータ構造から始めました:(x、y)座標の配列。私の配列はそれらの座標を列に入れます(のrbind(x,y)代わりに使用したかのようにcbind(x,y))。これspで十分です。ライブラリは使用されません。あなたが詳細に行われているものをフォローしたい場合、私は、言って、あなたがから始める勧めn.shapes <- 2n.shapes.new <- 3p.mean <- 1。そのshapes場合、、shapes.stdなどはすべて簡単に検査できるほど小さくなります。これらすべてに対処するためのエレガントな「正しい」方法は、標準化された機能表現のクラスを作成することです。
whuber

1

あなたは任意の回転と拡張で多くを求めています!ハウスドルフ距離がどれほど役立つかはわかりませんが、確認してください。私のアプローチは、安価なデータを介してチェックするケースの数を減らすことです。たとえば、2つの折れ線の長さが整数比でない場合(整数/段階的なスケーリングを想定)、高価な比較をスキップできます。同様に、バウンディングボックス領域またはそれらの凸包領域が適切な比率であるかどうかを確認できます。始点から終点までの距離や角度など、重心に対して実行できる安価なチェックがたくさんあると思います。

その後、スケーリングを検出した場合は、それを元に戻し、非常に負荷の高いチェックを実行してください。

明確化:私はあなたが使っているパッケージを知りません。整数比では、両方の距離を除算し、結果が整数であるかどうかを確認し、そうでない場合は、その値を反転し(間違った順序を選択した可能性があります)、再確認します。整数または十分に近い値が得られた場合は、おそらくスケーリングが行われていると推測できます。または、2つの異なる形状にすることもできます。

境界ボックスについては、おそらくそれを表す四角形の反対側の点を得たので、それらから領域を取り出すことは単純な計算です。比率比較の背後にある原理は同じですが、結果が二乗されるだけです。それらをそのRパッケージからうまく取り出せない場合は、凸包を気にしないでください。それは単なるアイデアでした(とにかく十分に安くない)。


どうもありがとう。2つの折れ線の長さが整数比でないかどうかを検出する方法を説明していただけませんか?あなたがチェックの例を与えることができれば、「すてきな比率であるバウンディングボックス領域または凸包領域ならば」また、私は多くのことを感謝
Klausos Klausos

たとえば、空間データから空間境界ボックスを抽出する場合、2つのポイントを受け取るだけです:spl <-sp :: SpatialLines(list(Lines(Line(xy.sp)、ID = i)))b <-bbox(
spl

メインポストを拡張しました。
lynxlynxlynx

「整数または十分に近い値が得られた場合、おそらくスケーリングが行われていたと推測できます。」ユーザーは1.4程度のスケールを適用できなかったでしょうか?
ヘルマン・カリージョ

もちろんですが、私の想定は、特に後の編集で明確になりました。私はウェブマップスタイルのズームを想像していました。
lynxlynxlynx

1

これらのポリラインを比較する良い方法は、各頂点での一連の(距離、回転角度)としての表現に依存することです:ポイントP1, P2, ..., PNで構成されるラインの場合、そのようなシーケンスは次のようになります:

(距離(P1P2)、角度(P1、P2、P3)、距離(P2P3)、...、角度(P(N-2)、P(N-1)、PN)、距離(P(N-1 )PN))。

要件によると、2つの線は、対応するシーケンスが同じ(順序と角度方向を法とする)である場合にのみ等しくなります。数列の比較は簡単です。

各ポリラインシーケンスを1回だけ計算し、lynxlynxlynxによって提案されているように、同じ自明な特性(長さ、頂点数...)を持つポリラインのみについてシーケンスの類似性をテストすることにより、計算は本当に高速になります。


これは正しい考えです。ただし、実際に機能させるには、反射、内部方向、複数の接続されたコンポーネントの可能性、浮動小数点の丸め誤差など、多くの詳細に対処する必要があります。それらは私が提供したソリューションで説明されています。
whuber

はい、私は主なアイデアだけを説明しました。あなたの答えは非常に完全です(よくあるように:-)
julien
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.