Usando R para calcular a área de vários polígonos em um mapa que se cruza com outro polígono sobreposto

22

Eu tenho um shapefile baixado do Ordnance Survey que fornece limites da ala eleitoral (divisão) para um condado do Reino Unido. Utilizei com êxito o R para carregar o shapefile e plotei vários mapas usando ggplot2como descrito nesta pergunta . Está tudo funcionando muito bem.

Agora, eu gostaria de criar um novo polígono de forma arbitrária, adicioná-lo ao mapa e calcular a população que vive na área situada sob a forma, que pode cobrir ou cobrir parcialmente várias divisões. Eu tenho a população de cada divisão eleitoral e posso supor que a população de cada ala é uniformemente distribuída. Isso sugere as seguintes etapas.

1) Sobreponha uma nova forma no mapa que cubra parcialmente várias divisões eleitorais. Digamos que existem 3 divisões, por uma questão de argumento. Seria algo assim. [Editar: exceto que na imagem abaixo a forma abrange 5 divisões em vez de 3]

insira a descrição da imagem aqui

2) Calcule a porcentagem da área de cada uma dessas 3 divisões que cruzam com o polígono sobreposto.

3) Estime a população obtendo a porcentagem da área de cada divisão coberta pela forma sobreposta e multiplicando-a pela população de cada divisão.

Eu acho que provavelmente posso descobrir como criar o polígono e sobrepô-lo no mapa, ou seja, adicioná-lo ao quadro de dados existente usando a resposta útil para esta e outras perguntas. O pouco que me preocupa é a tarefa de calcular a porcentagem de cada divisão que é coberta pela forma sobreposta. As colunas late longno quadro de dados são aquelas figuras estranhas do OpenData do Ordnance Survey (leste e norte ou algo assim).

Portanto, minha primeira pergunta é: como eu iria encontrar a área (ou um subconjunto da área) dos polígonos que definem as fronteiras de uma divisão eleitoral usando esses dados? Como até mesmo um subconjunto significativo desse quadro de dados é grande, usei dputpara criar um arquivo de 500k ( que pode ser copiado e colado ou baixado daqui ) em vez de publicá-lo nesta pergunta. O mapa que forma a base da imagem acima foi criado com o seguinte:

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

Minha segunda pergunta é: estou usando as ferramentas certas? Atualmente, estou usando readShapePolyo maptoolspacote para ler o shapefile. Eu então uso fortifypara criar um quadro de dados de cerca de 130k linhas, adequado para uso em ggplot. Talvez eu devesse usar um pacote diferente se houver um com ferramentas úteis para esses processos?

Aprendiz lento
fonte

Respostas:

16

A resposta de Spacedman e as dicas acima foram úteis, mas não constituem em si uma resposta completa. Depois de algum trabalho de detetive da minha parte, cheguei mais perto de uma resposta, embora ainda não tenha conseguido entrar gIntersectionno caminho que desejo (veja a pergunta original acima). Ainda assim, eu consegui meu novo polígono no SpatialPolygonsDataFrame.

ATUALIZAÇÃO 11/11/2012: Parece que encontrei uma solução viável (veja abaixo). A chave era quebrar os polígonos em uma SpatialPolygonschamada ao usar a gIntersectionpartir do rgeospacote. A saída é assim:

[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%"

Inserir o polígono foi mais difícil do que eu pensava, porque, surpreendentemente, não parece haver um exemplo fácil de seguir para inserir uma nova forma em um shapefile existente derivado da Pesquisa sobre Ordnance. Eu reproduzi meus passos aqui na esperança de que seja útil para outra pessoa. O resultado é um mapa como este.

mapa mostrando o novo polígono sobreposto

Se / quando resolver o problema de interseção, editarei esta resposta e adicionarei as etapas finais, a menos que, é claro, alguém me derrote e forneça uma resposta completa. Enquanto isso, comentários / conselhos sobre minha solução até agora são bem-vindos.

Código a seguir.

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
      })
Aprendiz lento
fonte
Esta pergunta (e resposta) tem sido útil para mim. Agora library(scales)deve ser adicionado para fazer a transparência funcionar.
Irene
1
Obrigado. Eu acredito que há uma require(scales)ligação lá que fará o truque.
SlowLearner
15

Não use readShapePoly - ele ignora a especificação da projeção. Use readOGR do pacote sp.

Para operações geográficas, como a sobreposição de polígonos, consulte o pacote rgeos.

Literalmente, a última coisa que você deve fazer é jogar com fortify e ggplot. Mantenha seus dados em objetos da classe sp, plote-os com gráficos de base e deixe o açúcar ggplot até o final de um projeto e você precisará de alguns gráficos bonitos.

Spacedman
fonte
Obrigado pelas dicas; Vou olhar novamente para readOGR. Quanto ao ggplot, é o que acontece naturalmente quando eu o aprendi, como eu aprendi R - nunca se incomodou com gráficos básicos.
SlowLearner
1
Re seu comentário sobre objetos sp-class, isso parece crucial se eu quiser tirar proveito das funções do rgeos. Consegui fazer uma variedade de polígonos usando o seu exemplo na resposta vinculada, mas não consigo descobrir como adicionar um novo polígono a um quadro de dados espaciais existente. Eu brinquei um pouco com a @datasintaxe, mas não cheguei a lugar algum. Você tem alguma dica?
SlowLearner
4
Você pode unir dois quadros de dados de polígonos espaciais com cbind(part1,part2)se eles tiverem IDs de polígono exclusivos - caso contrário, você receberá um aviso e precisará usar spChFIDspara atribuir IDs de recurso de polígono exclusivos.
Spacedman