Bagaimana mengukur kesamaan objek SpatialLines


9

Saya membuat dua SpatialLinesobjek dalam R: angka.

Objek-objek ini dibuat dengan cara ini:

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

Sekarang saya ingin entah bagaimana menyimpulkan bahwa ini adalah garis yang sama diputar dan dibalik, dan bahwa perbedaan di antara mereka sama dengan 0 (yaitu bentuk sama dengan).

Untuk melakukan itu, seseorang dapat menggunakan maptoolspaket dan memutar baris # 1, misalnya:

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

Setiap baris yang diputar kemudian harus diperiksa versus baris # 2 menggunakan rgeospaket, misalnya:

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

Namun, ini cara yang sangat mahal secara komputasional untuk mencocokkan SpatialLinesobjek, terutama jika jumlah objeknya sekitar 1000.

Apakah ada cara pintar untuk melakukan pekerjaan ini?

PS Selain itu, pendekatan yang dijelaskan di atas tidak menjamin semua kemungkinan rotasi dan flips.

P.S2. Jika baris # 1 diperkecil sehubungan dengan baris # 2, perbedaan antara baris # 1 dan # 2 harus tetap sama dengan 0.

MEMPERBARUI:

masukkan deskripsi gambar di sini

Jawaban:


9

Setiap metode efektif yang bertujuan umum akan membakukan representasi bentuk sehingga tidak akan berubah saat rotasi, terjemahan, refleksi, atau perubahan sepele dalam representasi internal.

Salah satu cara untuk melakukan ini adalah mendaftar setiap bentuk yang terhubung sebagai urutan bolak panjang tepi dan (ditandatangani) sudut, mulai dari satu ujung. (Bentuknya harus "bersih" dalam arti tidak memiliki tepi panjang nol atau sudut lurus.) Untuk membuat invarian ini dalam refleksi, meniadakan semua sudut jika yang bukan nol pertama negatif.

(Karena setiap polyline terhubung dari n simpul akan memiliki n -1 tepi dipisahkan oleh sudut n -2, saya merasa nyaman dalam Rkode di bawah ini untuk menggunakan struktur data yang terdiri dari dua array, satu untuk panjang tepi $lengthsdan yang lainnya untuk sudut, .Segmen $anglesgaris tidak akan memiliki sudut sama sekali, jadi penting untuk menangani array panjang nol dalam struktur data seperti itu.)

Representasi seperti itu dapat dipesan secara leksikografis. Beberapa kelonggaran harus dibuat untuk kesalahan floating-point yang diakumulasikan selama proses standardisasi. Prosedur yang elegan akan memperkirakan kesalahan tersebut sebagai fungsi dari koordinat asli. Dalam solusi di bawah ini, metode yang lebih sederhana digunakan di mana dua panjang dianggap sama ketika mereka berbeda dengan jumlah yang sangat kecil pada basis relatif. Sudut dapat berbeda hanya dengan jumlah yang sangat kecil pada basis absolut.

Untuk membuatnya tidak berubah di bawah pembalikan orientasi yang mendasarinya, pilih representasi paling awal secara leksikografis antara polyline dan pembalikannya.

Untuk menangani multi-bagian polyline, atur komponennya dalam urutan leksikografis.

Untuk menemukan kelas kesetaraan di bawah transformasi Euclidean, maka,

  • Buat representasi standar dari bentuk.

  • Lakukan semacam leksikografis dari representasi standar.

  • Buat melewati urutan diurutkan untuk mengidentifikasi urutan representasi yang sama.

Waktu komputasi sebanding dengan O (n * log (n) * N) di mana n adalah jumlah fitur dan N adalah jumlah simpul terbesar dalam fitur apa pun. Ini efisien.

Mungkin perlu disebutkan secara sepintas bahwa pengelompokan pendahuluan berdasarkan sifat geometri invarian yang mudah dihitung, seperti panjang polyline, pusat , dan momen tentang pusat itu, sering dapat diterapkan untuk merampingkan seluruh proses. Satu hanya perlu menemukan subkelompok fitur kongruen dalam masing-masing kelompok awal tersebut. Metode lengkap yang diberikan di sini akan diperlukan untuk bentuk yang sebaliknya akan sangat mirip sehingga invarian sederhana seperti itu masih tidak akan membedakannya. Fitur sederhana yang dibangun dari data raster mungkin memiliki karakteristik seperti itu, misalnya. Namun, karena solusi yang diberikan di sini sangat efisien, sehingga jika seseorang akan pergi ke upaya mengimplementasikannya, mungkin akan bekerja dengan baik dengan sendirinya.


Contoh

Gambar sebelah kiri menunjukkan lima polyline ditambah 15 yang diperoleh dari yang melalui terjemahan acak, rotasi, refleksi, dan pembalikan orientasi internal (yang tidak terlihat). Figur tangan kanan mewarnai mereka sesuai dengan kelas ekivalensi Euclidean mereka: semua figur dengan warna yang sama adalah kongruen; warna yang berbeda tidak kongruen.

Angka

Rkode berikut. Ketika input diperbarui ke 500 bentuk, 500 bentuk ekstra (kongruen), dengan rata-rata 100 simpul per bentuk, waktu eksekusi pada mesin ini adalah 3 detik.

Kode ini tidak lengkap: karena Rtidak memiliki jenis leksikografi asli, dan saya tidak merasa ingin mengkodekan satu dari awal, saya hanya melakukan penyortiran pada koordinat pertama dari setiap bentuk standar. Itu akan baik-baik saja untuk bentuk acak yang dibuat di sini, tetapi untuk pekerjaan produksi, jenis leksikografis lengkap harus diterapkan. Fungsi order.shapeakan menjadi satu-satunya yang terpengaruh oleh perubahan ini. Inputnya adalah daftar bentuk standar sdan outputnya adalah urutan indeks ke dalam syang akan mengurutkannya.

#
# 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)))

Ketika seseorang memasukkan pelebaran acak (atau "isotheties") dalam kelompok transformasi, kelas ekivalensi adalah kelas kongruensi geometri afin . Komplikasi ini mudah ditangani: misalnya, standarisasi semua polyline untuk memiliki panjang unit total.
whuber

Terima kasih banyak. Hanya satu pertanyaan: Haruskah bentuk direpresentasikan sebagai SpatialLines atau SpatialPolygons?
Klausos Klausos

Poligon menciptakan komplikasi lain: batas-batasnya tidak memiliki titik akhir yang pasti. Ada banyak cara untuk mengatasinya, seperti menstandarkan representasi untuk mulai dari (katakanlah) titik yang mengurutkan pertama dalam urutan leksikografis xy dan melanjutkan dalam arah berlawanan arah di sekitar poligon. (Poligon yang terhubung secara topologi "bersih" hanya akan memiliki satu simpul seperti itu.) Apakah suatu bentuk dianggap sebagai poligon atau polyline tergantung pada jenis fitur yang diwakilinya: tidak ada cara intrinsik untuk mengatakan daftar titik tertutup apakah itu adalah dimaksudkan untuk menjadi polyline atau poligon.
whuber

Maaf untuk pertanyaan sederhana, tetapi saya harus menanyakannya untuk memahami contoh Anda. Objek bentuk.std Anda memiliki $ panjang dan $ sudut. Namun, jika saya menjalankan kode ini pada data xy saya (misalnya [1,] 3093,5 -2987,8 [2,] 3072,7 -2991,0 dll), ia tidak memperkirakan sudut, tidak pula menggambar bentuk. Jika saya menjalankan plot (bentuk [[1]]), maka saya dapat dengan jelas melihat polyline saya. Jadi, bagaimana saya harus menyimpan polyline di R untuk dapat menguji kode Anda pada data saya?
Klausos Klausos

Saya mulai dengan struktur data yang sama dengan yang Anda lakukan: array koordinat (x, y). Array saya meletakkan koordinat-koordinat itu ke dalam kolom (seolah-olah Anda telah menggunakan rbind(x,y)alih-alih cbind(x,y)). Itu yang Anda butuhkan: spperpustakaan tidak digunakan. Jika Anda ingin mengikuti apa yang dilakukan dalam detail, saya sarankan Anda mulai dengan, katakanlah, n.shapes <- 2, n.shapes.new <- 3, dan p.mean <- 1. Kemudian shapes,, shapes.stddll. Semuanya cukup kecil untuk mudah diperiksa. Cara elegan - dan "benar" - untuk menangani semua ini adalah membuat kelas representasi fitur standar.
whuber

1

Anda banyak meminta rotasi dan pelebaran acak! Tidak yakin seberapa bermanfaat jarak Hausdorff di sana, tetapi periksa. Pendekatan saya akan mengurangi jumlah kasus untuk diperiksa melalui data murah. Misalnya, Anda bisa melewatkan perbandingan mahal jika panjang dua linestrings bukan rasio bilangan bulat ( dengan asumsi skala bilangan bulat / lulus ). Anda juga dapat memeriksa apakah area kotak pembatas atau area cembung cembungnya berada dalam rasio yang bagus. Saya yakin ada banyak pemeriksaan murah yang bisa Anda lakukan terhadap centroid, seperti jarak atau sudut dari awal / akhir.

Hanya kemudian, jika Anda mendeteksi penskalaan, batalkan dan lakukan pemeriksaan yang sangat mahal.

Klarifikasi: Saya tidak tahu paket yang Anda gunakan. Dengan rasio integer yang saya maksudkan adalah Anda harus membagi kedua jarak, memeriksa apakah hasilnya bilangan bulat, jika tidak, balikkan nilai itu (bisa jadi Anda memilih urutan yang salah) dan periksa kembali. Jika Anda mendapatkan bilangan bulat atau cukup dekat, Anda dapat menyimpulkan bahwa mungkin ada penskalaan yang terjadi. Atau bisa jadi hanya dua bentuk yang berbeda.

Adapun kotak pembatas, Anda mungkin mendapat titik berlawanan dari persegi panjang yang mewakilinya, jadi mengeluarkan area dari mereka adalah aritmatika sederhana. Prinsip di balik perbandingan rasio adalah sama, hanya saja hasilnya akan kuadrat. Jangan repot-repot dengan convex hulls jika Anda tidak bisa mengeluarkannya dari paket R dengan baik, itu hanya sebuah ide (sepertinya tidak cukup murah kok).


Terima kasih banyak. Bisakah Anda menjelaskan bagaimana mendeteksi jika panjang dua linestrings bukan rasio bilangan bulat? Juga, saya sangat menghargai jika Anda dapat memberikan contoh untuk memeriksa "jika area kotak pembatas atau area cembung berada dalam rasio yang bagus"
Klausos Klausos

Misalnya, jika saya mengekstrak kotak pembatas spasial dari data spasial, maka saya hanya menerima dua poin: spl <- sp :: SpatialLines (daftar (Baris (Baris (xy.sp), ID = i))) b <- bbox ( spl)
Klausos Klausos

Memperpanjang pos utama.
lynxlynxlynx

"Jika Anda mendapatkan bilangan bulat atau cukup dekat, Anda dapat menyimpulkan bahwa mungkin ada penskalaan yang terjadi." Tidak bisakah pengguna menerapkan skala 1,4 atau lebih?
Germán Carrillo

Tentu, tetapi asumsi saya menjadi jelas, terutama dengan suntingan nanti. Saya membayangkan zoom gaya webmap, di mana satu dibatasi dengan baik.
lynxlynxlynx

1

Metode yang baik untuk membandingkan polyline ini adalah dengan mengandalkan pada representasi sebagai urutan (jarak, putar sudut) pada setiap simpul: Untuk garis yang terdiri dari titik P1, P2, ..., PN, urutan tersebut adalah:

(jarak (P1P2), sudut (P1, P2, P3), jarak (P2P3), ..., sudut (P (N-2), P (N-1), PN), jarak (P (N-1) ) PN)).

Menurut kebutuhan Anda, dua garis sama dengan jika dan hanya jika urutan yang sesuai adalah sama (modulo urutan dan arah sudut). Membandingkan urutan angka adalah sepele.

Dengan menghitung setiap urutan polyline hanya sekali dan, seperti yang disarankan oleh lynxlynxlynx, menguji kesamaan urutan hanya untuk polyline yang memiliki karakteristik sepele yang sama (panjang, jumlah simpul ...), perhitungannya harus sangat cepat!


Ini ide yang tepat. Agar benar-benar berfungsi, banyak detail yang perlu ditangani, seperti mengatasi pantulan, orientasi internal, kemungkinan beberapa komponen yang terhubung, dan kesalahan pembulatan titik mengambang. Mereka dibahas dalam solusi yang saya berikan.
whuber

Ya, saya hanya menggambarkan ide utama. Jawaban Anda sangat lengkap (seperti sering :-)
julien
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.