Berechnung des {minimalen} Abstands zwischen Polygonen in R.

9

Ich habe die Oberfläche von Artenverteilungen berechnet (Zusammenführen von Polygonen aus Shapefiles), aber da diese Fläche aus ziemlich weit entfernten Polygonen bestehen kann, möchte ich ein Maß für die Dispersion berechnen. Was ich bisher getan habe, ist, die Schwerpunkte jedes Polygons abzurufen, den Abstand zwischen diesen zu berechnen und diese zur Berechnung des Variationskoeffizienten zu verwenden, wie im folgenden Dummy-Beispiel;

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

Darstellung der drei Polygone und ihrer Schwerpunkte

Geben Sie hier die Bildbeschreibung ein

Ich bin mir nicht sicher, ob dieser Ansatz sehr nützlich ist, da in einigen Fällen einige der Polygone (wie das blaue im obigen Beispiel) im Vergleich zu den anderen ziemlich groß sind, wodurch der Abstand noch weiter vergrößert wird. Zum Beispiel hat der Schwerpunkt Australiens fast die gleiche Entfernung zu seinen westlichen Grenzen wie nach Papau.

Was ich gerne bekommen würde, ist ein Beitrag zu alternativen Ansätzen. Wie oder mit welcher Funktion kann ich beispielsweise den Abstand zwischen Polygonen berechnen?

Ich habe getestet, ob der obige SpatialPolygon-Datenrahmen in PointPatterns (ppp) konvertiert werden {spatstat}soll nndist() {spatstat}, um den Abstand zwischen allen Punkten berechnen zu können . Aber da ich es mit ziemlich großen Flächen zu tun habe (viele Polygone und große), wird die Matrix riesig und ich bin mir nicht sicher, wie ich weiterhin zum minimalen Abstand zwischen den Polygonen gelangen soll .

Ich habe mir auch die Funktion angesehen gDistance {rgeos}, aber ich denke, sie funktioniert nur mit projizierten Daten, was für mich ein Problem sein kann, da meine Bereiche mehrere Bereiche überschreiten können EPSG areas. Das gleiche Problem würde für die Funktion auftreten crossdist {spatstat}.

jO.
quelle
1
Würden Sie postgres/postgiszusätzlich verwenden R? Ich habe einen Workflow verwendet, in dem ich den größten Teil meiner Arbeit ausführe R, die Daten jedoch in einer Datenbank speichere, auf die ich mit zugegriffen habe sqldf. Dies ermöglicht es Ihnen, alle postgisFunktionen zu verwenden (deren Abstand zwischen Polygonen direkt ist)
DJQ
@djq: Danke für den Kommentar. Ja, ich würde es auf jeden Fall postgresversuchen :) Ich habe mit dem Aufbau einer Datenbank begonnen, aber aufgehört, als ich nicht wusste (nicht sah), wie ich den Workflow / die Geostaten zwischen der Datenbank und R...
jO verbinden soll.

Antworten:

9

Sie können diese Analyse im Paket "spdep" durchführen. Wenn Sie in den relevanten Nachbarfunktionen "longlat = TRUE" verwenden, berechnet die Funktion die Großkreisentfernung und gibt Kilometer als Entfernungseinheit zurück. Im folgenden Beispiel können Sie das resultierende Entfernungslistenobjekt ("dist.list") in eine Matrix oder einen data.frame zwingen. Die Berechnung von Zusammenfassungsstatistiken mit lapply ist jedoch recht effizient.

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) } ) )
Jeffrey Evans
quelle
Vielen Dank für Ihre Kommentare und den Einblick in das spdebPaket. Nur zur Verdeutlichung liefert dieser Ansatz die gleiche Ausgabe wie in meinem Beispiel, oder?
jO.
Nur für den Fall, dass Sie meinen obigen Kommentar nicht gesehen haben
jO.
Obwohl die Antwort nützlichen Code zum Berechnen von Abständen zwischen Schwerpunkten liefert, behandelt sie nicht den Mittelpunkt des OP, mit dem der Abstand zwischen den beiden nächstgelegenen Punkten der Polygongrenzen ermittelt wird.
Csfowler
Ein großer Polizist und eine schlechte Form für SE, aber ich kann momentan nicht die ganze Arbeit machen. Meine eigene Suche nach einer Antwort auf diese Frage scheint darauf hinzudeuten, dass die gDistance-Funktion von Bibliotheks-Rgeos das tut, was das OP beabsichtigt hat: den kürzesten Abstand zwischen den Kanten finden. Wenn ich in meiner Eile, eine enge Frist einzuhalten, OP oder Jeffrey Evans falsch interpretiert habe, entschuldige ich mich aufrichtig.
Csfowler