Calcolo della distanza {minima} tra i poligoni in R


9

Ho calcolato la superficie delle distribuzioni delle specie (unendo i poligoni dai shapefile), ma poiché quest'area può essere composta da poligoni abbastanza distanziati, vorrei calcolare una certa misura della dispersione. Quello che ho fatto finora è stato recuperare i centroidi di ciascun poligono, calcolare la distanza tra questi e usarli per calcolare il coefficiente di variazione, come nell'esempio fittizio di seguito;

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

Trama dei tre poligoni e dei loro centroidi

inserisci qui la descrizione dell'immagine

Non sono sicuro che questo approccio sia molto utile poiché in molti casi, alcuni dei poligoni (come quello blu nell'esempio sopra) sono piuttosto grandi rispetto al resto, aumentando così ulteriormente la distanza. Ad esempio, il centroide dell'Australia ha quasi la stessa distanza dai suoi confini occidentali rispetto a Papau.

Quello che vorrei ottenere è un input su approcci alternativi. Ad esempio, come o con quale funzione posso calcolare la distanza tra i poligoni?

Ho provato a convertire il frame di dati SpatialPolygon sopra in PointPatterns (ppp) {spatstat}per poter correre nndist() {spatstat}per calcolare la distanza tra tutti i punti. Ma dal momento che ho a che fare con aree abbastanza grandi (molti poligoni e grandi), la matrice diventa enorme e non sono sicuro di come continuare a raggiungere la distanza minima tra i poligoni .

Ho anche esaminato la funzione gDistance {rgeos}, ma penso che funzioni solo sui dati proiettati, il che può essere un problema per me poiché le mie aree possono attraversarne diverse EPSG areas. Lo stesso problema sorgerebbe per la funzione crossdist {spatstat}.


1
Considereresti di utilizzare postgres/postgisoltre a R? Ho utilizzato un flusso di lavoro in cui eseguo la maggior parte del mio lavoro R, ma memorizzo i dati in un database a cui accedo utilizzandosqldf . Ciò consente di utilizzare tutte le postgisfunzioni (la cui distanza tra i poligoni è diretta)
djq

@djq: grazie per aver commentato. Sì, ci proverei sicuramente :) Ho iniziato a costruire un database postgresma mi sono fermato quando non sapevo (non guardavo) come collegare il flusso di lavoro / geostati tra il database e R...
jO.

Risposte:


9

È possibile eseguire questa analisi nel pacchetto "spdep". Nelle relative funzioni adiacenti, se si utilizza "longlat = TRUE", la funzione calcola la grande distanza del cerchio e restituisce chilometri come unità di distanza. Nell'esempio seguente è possibile forzare l'oggetto dell'elenco delle distanze risultante ("dist.list") in una matrice o data.frame, tuttavia è abbastanza efficiente calcolare le statistiche di riepilogo usando lapply.

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

Grazie per i commenti e l'intuizione del spdebpacchetto. Solo per chiarimenti, questo approccio produce lo stesso output del mio esempio, giusto?
jO.

Nel caso in cui non hai visto il mio commento sopra
jO.

Sebbene la risposta fornisca un codice utile per il calcolo delle distanze tra i centroidi, non si occupa del punto centrale dell'OP, che è come trovare la distanza tra i due punti più vicini dei bordi del poligono.
csfowler,

Un poliziotto enorme e una cattiva forma per SE, ma non riesco a fare tutto il lavoro in questo momento. La mia ricerca di una risposta a questa domanda sembra indicare che la funzione gDistance dai rgeos della libreria farà ciò che l'OP intendeva: trovare la distanza più breve tra i bordi. Se, nella fretta di rispettare una scadenza, ho interpretato male OP o Jeffrey Evans, mi scuso sinceramente.
csfowler,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.