Предположим, у меня есть файл данных с именем zone с 1994 строками координаторов 2D, обозначающих координаты вершин многоугольников, как показано ниже (самое первое число справа в каждой строке обозначает zone)

c1 <- "1", "1 21, 31 50, 45 65, 75 80"

c2 <- "2", "3 20, 5 15, 2 26, 70 -85, 40 50, 60 80"

.....

c1993 <- "1993", "3 2, 2 -5, 0 60, 7 -58, -12 23, 56 611, 85 152"

c1994 <- "1994", "30 200, 50 -15, 20 260, 700 -850, -1 2, 5 6, 8 15"

Теперь я хочу манипулировать этими строками таким образом, чтобы с учетом случайной пары lat-lon (скажем, 12 и 20) я мог бы сравнить, чтобы увидеть, попадает ли она в первый многоугольник, второй многоугольник, третий многоугольник, .... или 1994-й многоугольник. Решение грубой силы : сравните x-coordinate (= 12) со всеми 4 x - координатами и y-coordinate ( = 20) to all the 4 y -coordinates in c1 and c2 , respectively. The conclusion would be whether there is a valid **sandwich** inequality for each given coordinate x and y`.

Например, при использовании описанного выше процесса решения точка (12,20) будет в c1, но не в c2.

Мой вопрос: Как мне достичь этой цели в R?

Моя попытка: благодаря помощи Стефана Лорана мне удалось сгенерировать все матрицы, каждая с определенными размерами, в которых хранятся пары lat-lon всех вершин каждого многоугольника с помощью следующего кода :

 zone <- read_delim("[directory path to zone.csv file]", delim = ",", col_names = TRUE)
for(i in 1:nrow(zone)){
  zone$geo[i] = substr(zone$geo[i],10,135)
}
zone <- zone[complete.cases(zone),]

 Numextract <- function(string){
    unlist(regmatches(string, gregexpr("[[:digit:]]+\\.*[[:digit:]]*", string)))
 }

for(i in 1:nrow(zone)){
        poly1 <- matrix(as.numeric(Numextract(zone$geo[i])),i, ncol=2, byrow=TRUE)
        poly2 <- cbind(poly1, c(i))
}

Однако, как вы могли заметить, мне нужно найти способ проиндексировать все матрицы, соответствующие каждой зоне, которые были сгенерированы во время цикла for(). Причина в том, что впоследствии я могу использовать другой цикл for(), чтобы определить, к какой зоне принадлежит точка !! Но я не смог понять это, так что может кто-нибудь помочь мне с подробным кодом?

Фактический набор данных
Набор данных по зонам и полигонам

Набор данных пар широта и долгота

1
user177196 14 Апр 2018 в 09:41

1 ответ

Лучший ответ

Сначала определите свои многоугольники как матрицы, каждая строка которых представляет вершину:

poly1 <- rbind(c(1,21), c(31,50), c(45,65), c(75,80))
poly2 <- rbind(c(3,20), c(5,15), c(2,26), c(70,-85))

Определите точку для тестирования:

point <- c(12,20)

Теперь используйте функцию pip2d пакета ptinpoly:

> library(ptinpoly)
> pip2d(poly1, rbind(point))
[1] -1
> pip2d(poly2, rbind(point))
[1] 1

Это означает (см. ?pip2d), что точка находится вне poly1 и внутри poly2.

Обратите внимание на rbind(point) в pip2d. Мы используем rbind, потому что в более общем случае мы можем запустить тест для нескольких точек в одном и том же многоугольнике.

Если вам нужна помощь в преобразовании

c1 <- "1 21, 31 50, 45 65, 75 80"

К

poly1 <- rbind(c(1,21), c(31,50), c(45,65), c(75,80))

Тогда, возможно, тебе стоит задать другой вопрос.

Редактировать

Хорошо, не открывайте еще один вопрос. Вы можете действовать следующим образом.

c1 <- "1 21, 31 50, 45 65, 75 80"

Numextract <- function(string){
  unlist(regmatches(string, gregexpr("[[:digit:]]+\\.*[[:digit:]]*", string)))
}

poly1 <- matrix(as.numeric(Numextract(c1)), ncol=2, byrow=TRUE)

Который дает:

> poly1
     [,1] [,2]
[1,]    1   21
[2,]   31   50
[3,]   45   65
[4,]   75   80

2-й править

Для вашей второй проблемы ваши данные слишком велики. Единственное решение, которое я вижу, - это разбить данные на более мелкие части.

Но, во-первых, кажется, что функция pip2d также вызывает сбой сеанса R. Так что используйте другую функцию: pnt.in.poly из пакета SDMTools.

Вот небольшая модификация этой функции, ускоряющая ее за счет удаления ненужных выходных данных:

library(SDMTools)
pnt.in.poly2 <- function(pnts, poly.pnts){
  if (poly.pnts[1, 1] == poly.pnts[nrow(poly.pnts), 1] && 
      poly.pnts[1, 2] == poly.pnts[nrow(poly.pnts), 2]){ 
    poly.pnts = poly.pnts[-1, ]
  }
  out = .Call("pip", pnts[, 1], pnts[, 2], nrow(pnts), poly.pnts[,1], poly.pnts[, 2], nrow(poly.pnts), PACKAGE = "SDMTools")
  return(out)
}

Теперь, как было сказано ранее, разделите lat_lon на более мелкие части, длиной по 1 миллиону каждая (кроме последнего, меньшего размера):

lat_lon_list <- vector("list", 70)
for(i in 1:69){
  lat_lon_list[[i]] = lat_lon[(1+(i-1)*1e6):(i*1e6),]
}
lat_lon_list[[70]] <- lat_lon[69000001:nrow(lat_lon),]

Теперь запустите этот код:

library(data.table)
for(i in 1:70){
  DT <- data.table(V1 = pnt.in.poly2(lat_lon_list[[i]], polys[[1]]))
  for(j in 2:length(polys)){
    DT[, (sprintf("V%d",j)):=pnt.in.poly2(lat_lon_list[[i]], polys[[j]])]
  }
  fwrite(DT, sprintf("results%02d.csv", i))
  rm(DT)
}

Если он работает, он должен сгенерировать 70 файлов CSV, result01.csv, ..., result70.csv, каждый размером 1000000x1944 (кроме последнего, меньшего), тогда их можно открыть в Excel.

3-е изменение

Я пробовал код и получил ошибку: Error: cannot allocate vector of size 7.6 Mb.

Нам нужно более тонкое разделение:

lat_lon_list <- vector("list", 2*69+1)
for(i in 1:(2*69)){
  lat_lon_list[[i]] = lat_lon[(1+(i-1)*1e6/2):(i*1e6/2),]
}
lat_lon_list[[2*69+1]] <- lat_lon[69000001:nrow(lat_lon),]

for(i in 1:(2*69+1)){
  DT <- data.table(V1 = pnt.in.poly2(lat_lon_list[[i]], polys[[1]]))
  for(j in 2:length(polys)){
    DT[, (sprintf("V%d",j)):=pnt.in.poly2(lat_lon_list[[i]], polys[[j]])]
  }
  fwrite(DT, sprintf("results%02d.csv", i))
  rm(DT)
}
2
Stéphane Laurent 19 Апр 2018 в 18:38