Использование R для вычисления площади нескольких многоугольников на карте, которые пересекаются с другим наложенным многоугольником

22

У меня есть шейп-файл, загруженный из Обзора артиллерии, который дает границы избирательного округа (округа) для округа Соединенного Королевства. Я успешно использовал R для загрузки шейп-файла и нанес на карту различные карты, используя, ggplot2как описано в этом вопросе . Все работает довольно хорошо.

Теперь я хотел бы создать новый многоугольник произвольной формы, добавить его на карту, а затем рассчитать население, проживающее в области, лежащей под формой, которая может охватывать или частично покрывать несколько делений. У меня есть население для каждого избирательного подразделения, и я могу сделать упрощающее предположение, что население в каждой палате распределено равномерно. Это предполагает следующие шаги.

1) Наложите новую форму на карту, которая частично покрывает несколько избирательных округов. Допустим, есть 3 подразделения, ради аргумента. Это будет выглядеть примерно так. [Редактировать: за исключением того, что на изображении ниже фигуры колеблется 5 делений, а не 3]

введите описание изображения здесь

2) Рассчитайте процентную долю площади каждого из этих 3 делений, которая пересекается с наложенным многоугольником.

3) Оцените численность населения, получив процентную долю площади каждого подразделения, покрытой наложенной формой, и умножив ее на численность населения каждого подразделения.

Я думаю, что, возможно, смогу решить, как создать многоугольник и наложить его на карту, т.е. добавить его в существующий фрейм данных, используя полезный ответ на этот и другие вопросы. Немного, что меня беспокоит, так это задача определения процента каждого деления, покрытого наложенной формой. latИ longстолбцы в кадре данных являются теми странными фигурами Картографического OpenData (Eastings и Northings или что - то).

Итак, мой первый вопрос: как мне найти область (или подмножество области) полигонов, которые определяют границы избирательного деления, используя эти данные? Поскольку даже значимое подмножество этого фрейма данных велико, я использовал его dputдля создания файла размером 500 Кб ( который можно скопировать, вставить или загрузить отсюда ) вместо того, чтобы публиковать его в этом вопросе. Карта, которая составляет основу для изображения выше, была создана со следующим:

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

Мой второй вопрос: я использую правильные инструменты? В настоящее время я использую readShapePolyиз maptoolsпакета, чтобы прочитать шейп-файл. Затем я использую fortifyдля создания фрейма данных около 130 тыс. Строк, пригодных для использования в ggplot. Может быть, я должен использовать другой пакет, если есть один с полезными инструментами для таких процессов?

SlowLearner
источник

Ответы:

16

Ответ Spacedman и подсказки выше были полезны, но сами по себе не являются полным ответом. После некоторой детективной работы с моей стороны, я приблизился к ответу, хотя мне еще не удалось встать gIntersectionтак, как я хочу (см. Оригинальный вопрос выше). Тем не менее, я уже успел получить свой новый полигон в SpatialPolygonsDataFrame.

ОБНОВЛЕНИЕ 2012-11-11: Кажется, я нашел работоспособное решение (см. Ниже). Ключ должен был обернуть многоугольники в SpatialPolygonsвызове при использовании gIntersectionиз rgeosпакета. Вывод выглядит так:

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

Вставить многоугольник было сложнее, чем я думал, потому что, как ни странно, нет простого примера вставки новой формы в существующий шейп-файл, полученный из Ordnance Survey. Я воспроизвел мои шаги здесь в надежде, что это будет полезно для кого-то еще. В результате получается такая карта.

карта, показывающая наложение нового многоугольника

Если / когда я решу проблему пересечения, я отредактирую этот ответ и добавлю заключительные шаги, если, конечно, кто-то не побьет меня и предоставит полный ответ. Тем временем, комментарии / советы относительно моего решения пока приветствуются.

Кодекс следует.

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
      })
SlowLearner
источник
Этот вопрос (и ответ) был полезен для меня. Теперь library(scales)нужно добавить, чтобы прозрачность работала.
Ирэн
1
Спасибо. Я верю, что там есть require(scales)звонок, который поможет.
SlowLearner
15

Не используйте readShapePoly - он игнорирует спецификацию проекции. Используйте readOGR из пакета sp.

Для географических операций, таких как наложение полигонов, проверьте пакет rgeos.

Буквально последнее, что вы должны сделать, это поиграть с fortify и ggplot. Храните ваши данные в объектах sp-класса, наносите их с помощью базовой графики и оставляйте сахар ggplot до конца проекта, и вам понадобятся несколько симпатичных графиков.

Spacedman
источник
Спасибо за советы; Я снова посмотрю на readOGR. Что касается ggplot, это то, что естественно, поскольку я изучил это, поскольку я изучил R - никогда не беспокоился о базовой графике.
SlowLearner
1
Что касается ваших комментариев к объектам sp-класса, это кажется решающим, если я хочу воспользоваться функциями в rgeos. Мне удалось создать ассортимент полигонов, используя ваш пример в связанном ответе, но я не могу понять, как добавить новый полигон в существующий фрейм пространственных данных. Я возился немного с @dataсинтаксисом , но не получить в любом месте. Есть ли у вас какие-либо советы?
SlowLearner
4
Вы можете объединить два фрейма данных пространственного полигона, cbind(part1,part2)если у них есть уникальные идентификаторы полигонов - в противном случае вы получите предупреждение и должны будете использовать его spChFIDsдля назначения уникальных идентификаторов объектов полигонов.
Spacedman