Usare R per calcolare l'area di più poligoni su una mappa che si intersecano con un altro poligono sovrapposto


22

Ho scaricato un file di forma dal Ordnance Survey che fornisce i confini del reparto elettorale (divisione) per una contea del Regno Unito. Ho usato con successo R per caricare lo shapefile e tracciato varie mappe usando ggplot2come descritto in questa domanda . Funziona tutto piuttosto bene.

Ora vorrei creare un nuovo poligono di forma arbitraria, aggiungerlo alla mappa, quindi calcolare la popolazione che vive nell'area sotto la forma, che potrebbe coprire o coprire parzialmente più divisioni. Ho la popolazione per ogni divisione elettorale e posso dare per scontato che la popolazione di ogni rione sia distribuita uniformemente. Ciò suggerisce i seguenti passaggi.

1) Sovrapponi una nuova forma sulla mappa che copre parzialmente più divisioni elettorali. Diciamo che ci sono 3 divisioni, per ragioni di discussione. Sembrerebbe qualcosa del genere. [Modifica: tranne quello nell'immagine sotto la forma a cavallo tra 5 divisioni anziché 3]

inserisci qui la descrizione dell'immagine

2) Calcola la percentuale dell'area di ciascuna di queste 3 divisioni che si interseca con il poligono sovrapposto.

3) Stimare la popolazione ottenendo la percentuale dell'area di ciascuna divisione coperta dalla forma sovrapposta e moltiplicandola per la popolazione di ciascuna divisione.

Penso di poter probabilmente capire come creare il poligono e sovrapporlo sulla mappa, ovvero aggiungerlo al frame di dati esistente usando la risposta utile a questa e ad altre domande. La parte che mi preoccupa è il compito di calcolare la percentuale di ogni divisione coperta dalla forma sovrapposta. Le colonne late longnel frame di dati sono quelle strane figure OpenData di Ordnance Survey (Eastings e Northings o qualcosa del genere).

Quindi la mia prima domanda è: come farei per trovare l'area (o un sottoinsieme dell'area) dei poligoni che definiscono i confini di una divisione elettorale usando questi dati? Poiché anche un sottoinsieme significativo di questo frame di dati è grande, ho usato dputper creare un file 500k ( che può essere copiato e incollato o scaricato da qui ) piuttosto che pubblicarlo in questa domanda. La mappa che costituisce la base per l'immagine sopra è stata creata con il seguente:

require(ggplot2)
ggplot(smalldf, aes(x = long, y = lat, group = group)) +
    geom_polygon(colour = "grey50", size = 1, aes(fill = smalldf$bin))

La mia seconda domanda è: sto usando gli strumenti giusti? Attualmente sto usando readShapePolydal maptoolspacchetto per leggere lo shapefile. Quindi utilizzo fortifyper creare un frame di dati di circa 130k righe, adatto per l'uso in ggplot. Forse dovrei usare un pacchetto diverso se ce n'è uno con strumenti utili per tali processi?

Risposte:


16

La risposta di Spacedman e i suggerimenti di cui sopra erano utili, ma non costituiscono di per sé una risposta completa. Dopo alcuni lavori investigativi da parte mia, mi sono avvicinato a una risposta, anche se non sono ancora riuscito a ottenere gIntersectionnel modo che desidero (vedere la domanda originale sopra). Tuttavia, sono riuscito a inserire il mio nuovo poligono in SpatialPolygonsDataFrame.

AGGIORNAMENTO 2012-11-11: mi sembra di aver trovato una soluzione praticabile (vedi sotto). La chiave era avvolgere i poligoni in una SpatialPolygonschiamata quando si utilizza gIntersectiondal rgeospacchetto. L'output è simile al seguente:

[1] "Haverfordwest: Portfield ED (poly 2) area = 1202564.3, intersect = 143019.3, intersect % = 11.9%"
[1] "Haverfordwest: Prendergast ED (poly 3) area = 1766933.7, intersect = 100870.4, intersect % = 5.7%"
[1] "Haverfordwest: Castle ED (poly 4) area = 683977.7, intersect = 338606.7, intersect % = 49.5%"
[1] "Haverfordwest: Garth ED (poly 5) area = 1861675.1, intersect = 417503.7, intersect % = 22.4%"

Inserire il poligono è stato più difficile di quanto pensassi perché, sorprendentemente, non sembra esserci un esempio facile da seguire per inserire una nuova forma in un file di forma derivato da un sondaggio di Ordnance esistente. Ho riprodotto i miei passi qui nella speranza che possa essere utile a qualcun altro. Il risultato è una mappa come questa.

mappa che mostra il nuovo poligono sovrapposto

Se / quando risolverò il problema dell'intersezione, modificherò questa risposta e aggiungerò i passaggi finali, a meno che, ovviamente, qualcuno non mi picchi e fornisca una risposta completa. Nel frattempo, commenti / consigli sulla mia soluzione finora sono tutti benvenuti.

Segue il codice.

require(sp) # the classes and methods that make up spatial ops in R
require(maptools) # tools for reading and manipulating spatial objects
require(mapdata) # includes good vector maps of world political boundaries.
require(rgeos)
require(rgdal)
require(gpclib)
require(ggplot2)
require(scales)
gpclibPermit()

## Download the Ordnance Survey Boundary-Line data (large!) from this URL:
## https://www.ordnancesurvey.co.uk/opendatadownload/products.html
## then extract all the files to a local folder.
## Read the electoral division (ward) boundaries from the shapefile
shp1 <- readOGR("C:/test", layer = "unitary_electoral_division_region")
## First subset down to the electoral divisions for the county of Pembrokeshire...
shp2 <- shp1[shp1$FILE_NAME == "SIR BENFRO - PEMBROKESHIRE" | shp1$FILE_NAME == "SIR_BENFRO_-_PEMBROKESHIRE", ]
## ... then the electoral divisions for the town of Haverfordwest (this could be done in one step)
shp3 <- shp2[grep("haverford", shp2$NAME, ignore.case = TRUE),]

## Create a matrix holding the long/lat coordinates of the desired new shape;
## one coordinate pair per line makes it easier to visualise the coordinates
my.coord.pairs <- c(
                    194500,215500,
                    194500,216500,
                    195500,216500,
                    195500,215500,
                    194500,215500)

my.rows <- length(my.coord.pairs)/2
my.coords <- matrix(my.coord.pairs, nrow = my.rows, ncol = 2, byrow = TRUE)

## The Ordnance Survey-derived SpatialPolygonsDataFrame is rather complex, so
## rather than creating a new one from scratch, copy one row and use this as a
## template for the new polygon. This wouldn't be ideal for complex/multiple new
## polygons but for just one simple polygon it seems to work
newpoly <- shp3[1,]

## Replace the coords of the template polygon with our own coordinates
newpoly@polygons[[1]]@Polygons[[1]]@coords <- my.coords

## Change the name as well
newpoly@data$NAME <- "zzMyPoly" # polygons seem to be plotted in alphabetical
                                 # order so make sure it is plotted last

## The IDs must not be identical otherwise the spRbind call will not work
## so use the spCHFIDs to assign new IDs; it looks like anything sensible will do
newpoly2 <- spChFIDs(newpoly, paste("newid", 1:nrow(newpoly), sep = ""))

## Now we should be able to insert the new polygon into the existing SpatialPolygonsDataFrame
shp4 <- spRbind(shp3, newpoly2)

## We want a visual check of the map with the new polygon but
## ggplot requires a data frame, so use the fortify() function
mydf <- fortify(shp4, region = "NAME")

## Make a distinction between the underlying shapes and the new polygon
## so that we can manually set the colours
mydf$filltype <- ifelse(mydf$id == 'zzMyPoly', "colour1", "colour2")

## Now plot
ggplot(mydf, aes(x = long, y = lat, group = group)) +
    geom_polygon(colour = "black", size = 1, aes(fill = mydf$filltype)) +
    scale_fill_manual("Test", values = c(alpha("Red", 0.4), "white"), labels = c("a", "b"))

## Visual check, successful, so back to the original problem of finding intersections
overlaid.poly <- 6 # This is the index of the polygon we added
num.of.polys <- length(shp4@polygons)
all.polys <- 1:num.of.polys
all.polys <- all.polys[-overlaid.poly] # Remove the overlaid polygon - no point in comparing to self
all.polys <- all.polys[-1] ## In this case the visual check we did shows that the
                           ## first polygon doesn't intersect overlaid poly, so remove

## Display example intersection for a visual check - note use of SpatialPolygons()
plot(gIntersection(SpatialPolygons(shp4@polygons[3]), SpatialPolygons(shp4@polygons[6])))

## Calculate and print out intersecting area as % total area for each polygon
areas.list <- sapply(all.polys, function(x) {
    my.area <- shp4@polygons[[x]]@Polygons[[1]]@area # the OS data contains area
    intersected.area <- gArea(gIntersection(SpatialPolygons(shp4@polygons[x]), SpatialPolygons(shp4@polygons[overlaid.poly])))
    print(paste(shp4@data$NAME[x], " (poly ", x, ") area = ", round(my.area, 1), ", intersect = ", round(intersected.area, 1), ", intersect % = ", sprintf("%1.1f%%", 100*intersected.area/my.area), sep = ""))
    return(intersected.area) # return the intersected area for future use
      })

Questa domanda (e risposta) mi è stata utile. Ora library(scales)deve essere aggiunto per far funzionare la trasparenza.
Irene,

1
Grazie. Credo che ci sia una require(scales)chiamata lì dentro che farà il trucco.
SlowLearner,

15

Non usare readShapePoly: ignora le specifiche di proiezione. Usa readOGR dal pacchetto sp.

Per operazioni geografiche come il tuo overlay poligonale, controlla il pacchetto rgeos.

Letteralmente l'ultima cosa che dovresti fare è giocare con fortify e ggplot. Conserva i tuoi dati in oggetti di classe sp, tracciali con la grafica di base e lascia lo zucchero ggplot fino alla fine di un progetto e avrai bisogno di trame piuttosto carine.


Grazie per i suggerimenti; Guarderò di nuovo readOGR. Per quanto riguarda ggplot, è ciò che viene naturale quando l'ho imparato mentre imparavo R, mai disturbato dalla grafica di base.
SlowLearner,

1
Per quanto riguarda il tuo commento sugli oggetti di classe sp, questo sembra cruciale se voglio sfruttare le funzioni di rgeos. Sono riuscito a creare un assortimento di poligoni usando il tuo esempio nella risposta collegata, ma non riesco a capire come aggiungere un nuovo poligono a un frame di dati spaziali esistente. Ho fatto un po 'casino con la @datasintassi ma non sono arrivato da nessuna parte. Hai qualche consiglio?
SlowLearner

4
Puoi unire due frame di dati poligonali spaziali con cbind(part1,part2)se hanno ID poligonali univoci, altrimenti ricevi un avviso e devi usarli spChFIDsper assegnare ID di funzionalità poligonali univoci.
Spacedman
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.