R gis: добавить атрибут полигона на основе значения соседей? - PullRequest
4 голосов
/ 23 января 2020

У меня есть набор древостоев (SpatialPolygonDataFrame), распределенных случайным образом по ландшафту, то есть разбросанных и агрегированных. Для каждого многоугольника я хочу решить, имеет ли он открытый край или нет. Полигон имеет открытый край , если:

  • не имеет соседей
  • не имеет соседей хотя бы на одной стороне;
  • имеет соседей, но разница между высотой дерева между трибунами и соседями составляет более 5

Мне интересно, как добавить атрибут open_edge = TRUE/FALSE к отдельным полигонам? В пакете raster есть многообещающий подход с использованием moving window. Однако мои исходные данные являются классами пространственных объектов, и, к сожалению, они не такие растровые, как в рабочем примере.

Хотя я ( псевдокод ):

  • подмножество каждого стенда один за другим (в for l oop)
  • создание окружающего буфера
  • подмножество окружающего стенда путем перекрытия buffer с подставками
  • , если Любые соседи -> сравните высоту. Если разница> 5, open_edge = TRUE

Но в этом подходе не учитывается то, что подставка имеет, скажем, соседей только по 3 сторонам, то есть как соседство ладьи. Инструмент poly2nb выглядит многообещающе, но как добавить атрибуты на отдельные стенды?

enter image description here

Вот мой глупый подход, но мне интересно, есть ли у вас более эффективное решение?

Создать фиктивные данные:

library(ggplot2)  # for choropleth map plot
library(broom) # to convert spatial data to dataframe
library(mapproj)
library(spdep)    # neighbours
library(rgdal)
library(rgeos)
library(sf)
library(raster)
library(dplyr)
library(spData)
library(sf)

r <- raster(nrow=6, ncol=6, crs = "+init=epsg:2957")
values(r) <- matrix(data = c(9,  NA, NA, NA, NA, NA,
                             NA, NA, NA, NA, NA, NA, 
                             NA, NA, NA, NA, NA, NA, 
                             NA, NA, NA, 1, 1, 1,
                             NA, NA, NA, 1, 9, 1,
                             NA, NA, NA, 1, 1, 1),
                    nrow = 6,
                    ncol = 6, 
                    byrow = TRUE)

# Convert raster to polygon
polys <- rasterToPolygons(r)

Определить, имеет ли подставка открытый край, на примере одного стенда:

# Subset first row in SpatialPolygonDataFrame
i = 10
one = polys[i, ]

# Keep the remaining polygons
left = polys[-i,]

# Create buffer within distance
buff = buffer(one, width = 100)

# subset set of neighbours by spatial overlap
nbrs <- left[which(gContains(sp::geometry(buff),
                                    sp::geometry(left), byid = T)),    
# Compare if the values are different
height.one  = rep(one$layer[1], nrow(nbrs))
height.nbrs = nbrs$layer

# Get the differences between the neighbouring stands
difference = height.one - height.nbrs

# If the difference in at least one stand is 
# in more than 5, set open_edge = TRUE 
# or if no neighbours find
one$open_edge <- any(difference > 5)

Ответы [ 3 ]

1 голос
/ 25 января 2020

Кажется, что это более простое решение. Вы можете использовать focal функцию скользящего окна из пакета raster.

Вот пример:

library(raster)

r <- raster(nrow=6, ncol=6, crs = "+init=epsg:2957")
values(r) <- matrix(data = c(9,  NA, NA, NA, NA, NA,
                             NA, NA, NA, NA, NA, NA, 
                             NA, NA, 2, 1, 3, 1, 
                             NA, NA, 1, 1, 1, 1,
                             NA, NA, 1, 2, 2, 1,
                             NA, NA, 1, 1, 1, 1),
                    nrow = 6,
                    ncol = 6, 
                    byrow = TRUE)

# Prepare function for sliding window
focal_func <- function(x) {
    # Assuming 3x3 moving window
    # central cell has index 5
    # Check if the cell is not NA
    if (is.na(x[5])){
        return(NA)
    }

    # Check if any surrounding is NA
    if (any(is.na(x[-5]))) {
        return(TRUE)
    }

    # Check difference
    if (any((x[5] - x[-5]) > 5)) {
        return(TRUE)
    }

    # Else, return FALSE
    return(FALSE)
}

# Apply focal_function with sliding window
res <- focal(r, w = matrix(rep(1, 9), 3), fun = focal_func, pad = TRUE)

# Check if the same as desired output
res_mat <- as.matrix(res)
res[!is.na(res)] == 1

Это дает:

[1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE FALSE
[13]  TRUE  TRUE  TRUE  TRUE  TRUE

Т.е. такой же, как желаемый вывод.

1 голос
/ 24 января 2020

Чтобы начать с spdep::poly2nb

library(raster)

r <- raster(nrow=6, ncol=6, crs = "+init=epsg:2957")
values(r) <- matrix(data = c(9,  NA, NA, NA, NA, NA,
                             NA, NA, NA, NA, NA, NA, 
                             NA, NA, NA, NA, NA, NA, 
                             NA, NA, NA, 1, 1, 1,
                             NA, NA, NA, 1, 9, 1,
                             NA, NA, NA, 1, 1, 1),
                    nrow = 6,
                    ncol = 6, 
                    byrow = TRUE)

# Convert raster to polygon
polys <- rasterToPolygons(r)

library(spdep)
nb <- poly2nb(polys)

plot(polys)
text(polys, 1:length(polys))

str(nb)
#List of 10
# $ : int 0
# $ : int [1:3] 3 5 6
# $ : int [1:5] 2 4 5 6 7
# $ : int [1:3] 3 6 7
# ...

Таким образом, у poly 1 нет соседей, у poly 2 есть соседи 3, 5, 6 и т. Д. c.

Теперь вы можно использовать sapply для вычисления вещей. Например, число соседей

nbcnt <- sapply(nb, function(i) length(i[i>0]))
nbcnt 
#[1] 0 3 5 3 5 8 5 3 5 3

Чтобы добавить это обратно в полигоны

polys$nbcnt <- nbcnt
0 голосов
/ 25 января 2020

На основе ответа @RobertHijmans и как получить список соседей Я создал пошаговый подход для проверки соседей и оценки разницы в высоте.

Шаг за шагом:

  1. проверяет, сколько соседей имеет ячейка. Если число соседей <8, установите <code>open_edge как TRUE, в противном случае FALSE
  2. L oop через индекс ячеек у которых есть 8 соседей (случай королевы): сравните разницу в высоте между центральной и окружающей клетками. Если какое-либо различие составляет> 5, то для open_edge устанавливается значение TRUE.

Вот несколько более сложная ситуация, позволяющая большему количеству стендов иметь соседей: enter image description here

Создание фиктивных данных:

r <- raster(nrow=6, ncol=6, crs = "+init=epsg:2957")
values(r) <- matrix(data = c(9,  NA, NA, NA, NA, NA,
                             NA, NA, NA, NA, NA, NA, 
                             NA, NA, 2, 1, 3, 1, 
                             NA, NA, 1, 1, 1, 1,
                             NA, NA, 1, 2, 2, 1,
                             NA, NA, 1, 1, 1, 1),
                    nrow = 6,
                    ncol = 6, 
                    byrow = TRUE)

# Convert raster to polygon
polys <- rasterToPolygons(r)

Вычисление соседей на основе непрерывности с учетом возможных промежутков или щелей между клетками:

# Calculate the count and position of neighbors; this allows to create one by one list
nb <- poly2nb(polys, 
              snap = 10) # snap corrects for the gaps/slivers

# store the number of neighbors for each central cell
polys$nb_count<- card(nb)

# Has the stand an open edge? Is surrounded by neighbors, pre-value is FALSE
polys$open_edge = ifelse(card(nb) <8, "TRUE", "FALSE")

Если ячейка имеет полных соседей, сравните различия по высоте между ними:

# Get the position of the cell surrounded by neighbors
center.index <- which(polys$nb_count == 8)

# Get the stand height of a stand
# as a vector to compare element wise
center.height = polys[center.index,]$layer

# Loop through the cells with neighbors:
# - keep height of the central stand
# - get height of neighbors
# compare the height between them
# if difference is more than 5: => open_edge = TRUE
for (i in seq_along(center.index)) {

  # Get central stand height 
  center.height = polys[center.index[i],]$layer

  # Identify neighbors of the stands
  # by the index value
  nb.index = unlist(nb[center.index[i]])

  # Get heights of the stands
  nb.height = polys[nb.index,]$layer

  # Adjust Center.height length as a vector to allow element wise comparison
  center.height.v = rep(center.height, length(nb.index))

  # Compare the heights 
  h.diff = center.height.v - nb.height

  # if any difference is more than 5 => change open_edge = TRUE
  if (any(h.diff > 5)) {
    polys@data[center.index[i],]$open_edge <- "TRUE"
  }
}

Посмотрите на окончательный вывод данных:

> polys@data
   layer nb_count open_edge
1      9        0      TRUE
2      2        3      TRUE
3      1        5      TRUE
4      3        5      TRUE
5      1        3      TRUE
6      1        5      TRUE
7      1        8     FALSE
8      1        8     FALSE
9      1        5      TRUE
10     1        5      TRUE
11     2        8     FALSE
12     2        8     FALSE
13     1        5      TRUE
14     1        3      TRUE
15     1        5      TRUE
16     1        5      TRUE
17     1        3      TRUE
...