使用R计算地图上与另一个重叠多边形相交的多个多边形的面积


22

我从军械测量局下载了一个shapefile,该文件提供了英国一个县的选举区(分区)边界。我已经成功地使用R加载了shapefile,并ggplot2按照此问题中的描述绘制了各种地图。一切都很好。

现在,我想创建一个任意形状的新多边形,将其添加到地图上,然后计算居住在该形状下的区域中的人口,该区域可能覆盖或部分覆盖多个分区。我有每个选举分区的人口,我可以简化假设,即每个病房的人口是均匀分布的。这表明了以下步骤。

1)在地图上覆盖部分覆盖多个选举区的新形状。出于辩论的原因,假设有3个分区。看起来像这样。[编辑:除了在下面的图形中,跨度为5格而不是3格]

在此处输入图片说明

2)计算与覆盖的多边形相交的这三个分区中每个分区的面积百分比。

3)通过获得覆盖形状覆盖的每个分区的面积百分比,然后乘以每个分区的人口,估算人口。

我想我可能可以解决如何创建多边形并将其覆盖在地图上的问题,即使用对此问题和其他问题的有用答案将其添加到现有数据框中。让我担心的一点是,要计算出覆盖形状所覆盖的每个分区的百分比。数据框中的latlong列是那些奇怪的Ordnance Survey OpenData数据(Eastings和Northings等)。

所以我的第一个问题是:如何使用这些数据查找定义选举区划边界的多边形的面积(或面积的子集)?因为即使该数据帧的一个有意义的子集都很大,所以我习惯于dput创建一个500k的文件(可以从此处复制和粘贴或下载该文件),而不是在此问题中发布它。使用以下内容创建了构成以上图像基础的地图:

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

我的第二个问题是:我在使用正确的工具吗?目前,我正在使用readShapePolymaptools包装中读取shapefile的文件。然后fortify,我使用来创建大约130k行的数据框,适用于ggplot。如果有一个针对此类过程的有用工具,也许我应该使用其他软件包?

Answers:


16

以上的Spacedman的答案和提示很有用,但它们本身并不构成完整的答案。经过我自己的一些侦探工作,尽管我还没有设法达到gIntersection我想要的方式(参见上面的原始问题),但我已经接近答案了。尽管如此,我还是设法将新的多边形放入SpatialPolygonsDataFrame中。

更新2012-11-11:我似乎找到了一个可行的解决方案(请参阅下文)。关键是在从程序包中SpatialPolygons使用时将多边形包装在调用中。输出看起来像这样:gIntersectionrgeos

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

插入多边形比我想象的要难,因为令人惊讶的是,似乎没有一个容易遵循的例子,可以在现有的源自军械测量局的shapefile中插入新形状。我在这里转载了我的步骤,希望对其他人有用。结果是这样的地图。

地图显示新的多边形覆盖

如果/当我解决交叉路口问题时,我将编辑此答案并添加最终步骤,当然,除非有人击败我并提供完整答案。同时,到目前为止,欢迎提出有关我的解决方案的意见/建议。

代码如下。

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

这个问题(和答案)对我很有用。现在library(scales)必须添加以使透明度起作用。
艾琳

1
谢谢。我相信那里有一个require(scales)电话可以解决这个问题。
SlowLearner 2014年

15

不要使用readShapePoly-它会忽略投影规范。使用sp软件包中的readOGR。

对于地理操作(例如多边形叠加层),请签出rgeos软件包。

从字面上看,您应该做的最后一件事是使用设防和ggplot。将数据保留在sp类对象中,使用基础图形对其进行绘制,然后将ggplot糖保留到项目结束为止,您需要一些漂亮的图形。


感谢您的提示;我将再次看一下readOGR。至于ggplot,这是我在学习R的过程中自然而然得到的-从来不打扰基础图形。
SlowLearner 2012年

1
关于sp类对象的评论,如果我想利用中的函数,这似乎至关重要rgeos。我已经在链接的答案中使用您的示例设法完成了多边形的分类,但是我不知道如何向现有的空间数据框中添加新的多边形。我在@data语法上有些混乱,但是一无所获。你有什么建议吗?
SlowLearner 2012年

4
如果两个空间多边形数据框cbind(part1,part2)具有唯一的多边形ID,则可以将它们连接在一起-否则会出现警告,需要使用它spChFIDs来分配唯一的多边形要素ID。
Spacedman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.