Обчислення {мінімальної} відстані між багатокутниками в R


9

Я підрахував площу поверхні розподілу видів (злиття полігонів із формними файлами), але оскільки ця площа може складатися з досить віддалених багатокутників, я б хотів обчислити деяку міру дисперсії. Що я вже робив, це отримати центроїди кожного багатокутника, обчислити відстань між ними та використати їх для обчислення коефіцієнта варіації, як у наведеному нижче прикладі;

require(sp)
require(ggplot2)
require(mapdata)
require(gridExtra)
require(scales)
require(rgeos)
require(spatstat)

# Create the coordinates for 3 squares
ls.coords <- list()
ls.coords <- list()
ls.coords[[1]] <- c(15.7, 42.3, # a list of coordinates
                    16.7, 42.3,
                    16.7, 41.6,
                    15.7, 41.6,
                    15.7, 42.3)

ls.coords[[2]] <- ls.coords[[1]]+0.5 # use simple offset

ls.coords[[3]] <- c(13.8, 45.4, # a list of coordinates
                    15.6, 45.4,
                    15.6, 43.7,
                    13.8, 43.7,
                    13.8, 45.4)

# Prepare lists to receive the sp objects and data frames
ls.polys <- list()
ls.sp.polys <- list()

for (ii in seq_along(ls.coords)) {
   crs.args <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
   my.rows <- length(ls.coords[[ii]])/2
   # create matrix of pairs
   my.coords <- matrix(ls.coords[[ii]],nrow = my.rows,ncol = 2,byrow = TRUE)
   # now build sp objects from scratch...
   poly = Polygon(my.coords)
   # layer by layer...
   polys = Polygons(list(poly),1)
   spolys = SpatialPolygons(list(polys))
   # projection is important
   proj4string(spolys) <- crs.args
   # Now save sp objects for later use
   ls.sp.polys[[ii]] <- spolys
   # Then create data frames for ggplot()
   poly.df <- fortify(spolys)
   poly.df$id <- ii
   ls.polys[[ii]] <- poly.df
}

# Convert the list of polygons to a list of owins
w <- lapply(ls.sp.polys, as.owin)
# Calculate the centroids and get the output to a matrix
centroid <- lapply(w, centroid.owin)
centroid <- lapply(centroid, rbind)
centroid <- lapply(centroid, function(x) rbind(unlist(x)))
centroid <- do.call('rbind', centroid)

# Create a new df and use fortify for ggplot
centroid_df <- fortify(as.data.frame(centroid))
# Add a group column
centroid_df$V3 <- rownames(centroid_df)

ggplot(data = italy, aes(x = long, y = lat, group = group)) +
  geom_polygon(fill = "grey50") +
  # Constrain the scale to 'zoom in'
  coord_cartesian(xlim = c(13, 19), ylim = c(41, 46)) +
  geom_polygon(data = ls.polys[[1]], aes(x = long, y = lat, group = group), fill = alpha("red", 0.3)) +
  geom_polygon(data = ls.polys[[2]], aes(x = long, y = lat, group = group), fill = alpha("green", 0.3)) +
  geom_polygon(data = ls.polys[[3]], aes(x = long, y = lat, group = group), fill = alpha("lightblue", 0.8)) + 
  coord_equal() +
  # Plot the centroids
  geom_point(data=centroid_points, aes(x = V1, y = V2, group = V3))

# Calculate the centroid distances using spDists {sp}
centroid_dists <- spDists(x=centroid, y=centroid, longlat=TRUE)

centroid_dists

       [,1]      [,2]     [,3]
[1,]   0.00000  69.16756 313.2383
[2,]  69.16756   0.00000 283.7120
[3,] 313.23834 283.71202   0.0000

# Calculate the coefficient of variation as a measure of polygon dispersion 
cv <- sd(centroid_dist)/mean(centroid_dist)
[1] 0.9835782

Сюжет трьох полігонів та їх центроїдів

введіть тут опис зображення

Я не впевнений, чи дуже корисний такий підхід, оскільки в багатьох випадках багатокутники (як блакитний у наведеному вище прикладі) досить великі порівняно з рештою, тим самим збільшуючи відстань ще більше. Наприклад, центроїд Австралії має майже таку ж відстань до західних кордонів, як і до Папау.

Я хотів би отримати деякий внесок у альтернативні підходи. Наприклад, як або за допомогою якої функції можна обчислити відстань між багатокутниками?

Я перевірив конвертувати вищевказаний кадр SpatialPolygon в PointPatterns (ppp), {spatstat}щоб можна було запустити nndist() {spatstat}обчислення відстані між усіма точками. Але оскільки я маю справу з досить великими площами (багато полігонів і великих), матриця стає величезною, і я не впевнений, як далі дістатися до мінімальної відстані між полігонами .

Я також роздивився функцію gDistance {rgeos}, але думаю, що вона працює лише на прогнозованих даних, що може бути проблемою для мене, оскільки мої області можуть перетинати декілька EPSG areas. Така ж проблема виникне і для функції crossdist {spatstat}.


1
Чи можете ви скористатися postgres/postgisдодатково R? Я використовував робочий процес, в якому виконую більшу частину своєї роботи R, але зберігаю дані в базі даних, до якої я отримую доступ sqldf. Це дає змогу використовувати всі postgisфункції (з яких відстань між багатокутниками пряма)
djq,

@djq: Дякую за коментар. Так, я б напевно postgresподумав :) Я почав створювати базу даних, але зупинився, коли не знав (не дивився), як з'єднати робочий процес / геостати між базою даних та R...
jO.

Відповіді:


9

Цей аналіз можна зробити в пакеті "spdep". У відповідних функціях сусідів, якщо ви використовуєте "longlat = ІСТИНА", функція обчислює велику відстань у колі та повертає кілометри як одиницю відстані. У наведеному нижче прикладі ви можете примусити отриманий об’єкт списку відстаней ("dist.list") до матриці або data.frame, однак, це досить ефективний обчислення зведеної статистики за допомогою пропуску.

require(sp)
require(spdep)

# Create SpatialPolygonsDataFrame for 3 squares
poly1 <- Polygons(list(Polygon(matrix(c(15.7,42.3,16.7,42.3,16.7,41.6,15.7,41.6,15.7,42.3), 
                   nrow=5, ncol=2, byrow=TRUE))),"1")     
poly2 <- Polygons(list(Polygon(matrix(c(15.7,42.3,16.7,42.3,16.7,41.6,15.7,41.6,15.7,42.3)+0.5, 
                   nrow=5, ncol=2, byrow=TRUE))),"2")     
poly3 <- Polygons(list(Polygon(matrix(c(13.8, 45.4, 15.6, 45.4,15.6, 43.7,13.8, 43.7,13.8, 45.4), 
                   nrow=5, ncol=2, byrow=TRUE))),"3")                      
spolys = SpatialPolygons(list(poly1,poly2,poly3),1:3)
 spolys <- SpatialPolygonsDataFrame(spolys, data.frame(ID=sapply(slot(spolys, "polygons"), 
                                    function(x) slot(x, "ID"))) )   
   proj4string(spolys) <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"

# Centroid coordinates (not used but provided for example) 
coords <- coordinates(spolys)

# Create K Nearest Neighbor list
skNN.nb <- knn2nb(knearneigh(coordinates(spolys), longlat=TRUE), 
                  row.names=spolys@data$ID)

# Calculate maximum distance for all linkages 
maxDist <- max(unlist(nbdists(skNN.nb, coordinates(spolys), longlat=TRUE)))

# Create spdep distance object
sDist <- dnearneigh(coordinates(spolys), 0, maxDist^2, row.names=spolys@data$ID)
  summary(sDist, coordinates(spolys), longlat=TRUE)

# Plot neighbor linkages                  
plot(spolys, border="grey") 
  plot(sDist, coordinates(spolys), add=TRUE)  

# Create neighbor distance list 
( dist.list <- nbdists(sDist, coordinates(spolys), longlat=TRUE) )

# Minimum distance 
( dist.min <- lapply(dist.list, FUN=min) )

# Distance coefficient of variation    
( dist.cv <- lapply(dist.list, FUN=function(x) { sd(x) / mean(x) } ) )

Дякуємо за коментар та розуміння spdebпакету. Тільки для уточнення, такий підхід дає той же результат, що і в моєму прикладі, правда?
jO.


Хоча відповідь дає корисний код для обчислення відстаней між центроїдами, він не стосується центральної точки ОП, яка полягає в тому, як знайти відстань між двома найближчими точками меж полігону.
csfowler

Величезний коп і погана форма для SE, але я не можу зараз виконати повноцінну роботу. Мій власний пошук відповіді на це запитання, схоже, вказує на те, що функція gDistance від бібліотечних rgeos виконає те, що призначено ОП: знайти найкоротшу відстань між ребрами. Якщо я поспішаю зіткнутися зі строгим терміном, я неправильно трактував ОП або Джефрі Еванс, мої щирі вибачення.
csfowler
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.