I have two GIS layers -- call them Soils
and Parcels
-- stored as SpatialPolygonsDataFrame
s (SPDF
s), and I would like to "overlay" them, in the sense described here.
The result of the overlay operation should be a new SPDF in which:
The
SpatialPolygons
component contains polygons formed by the intersection of the two layers. (Think all of the atomic polygons formed by overlaying two mylars on an overhead projector).The
data.frame
component records the attributes of theSoils
andParcels
polygons within which each atomic polygon falls.
My question(s): Is there an existing R function that does this? (I would even by happy to learn of a function that just gets the SpatialPolygons
component right, calculating the atomic polygons formed from the intersection of the two layers.) I feel like rgeos should have a function that does (1) at least, but it seems not to...
Here is a figure that may help make it clearer what I'm after, followed by code that creates the Soils
and Parcels
layers shown in the figure.
library(rgeos)
## Just a utility function to construct the example layers.
flattenSpatialPolygons <- function(SP) {
nm <- deparse(substitute(SP))
AA <- unlist(lapply(SP@polygons, function(X) X@Polygons))
SpatialPolygons(lapply(seq_along(AA),
function(X) Polygons(AA[X], ID=paste0(nm, X))))
}
## Example Soils layer
Soils <-
local({
A <- readWKT("MULTIPOLYGON(((3 .5,7 1,7 2,3 1.5,3 0.5), (3 1.5,7 2,7 3,3 2.5,3 1.5)))")
AA <- flattenSpatialPolygons(A)
SpatialPolygonsDataFrame(AA,
data.frame(soilType = paste0("Soil_", LETTERS[seq_along(AA)]),
row.names = getSpPPolygonsIDSlots(AA)))
})
## Example Parcels layer
Parcels <-
local({
B <- readWKT("MULTIPOLYGON(((0 0,2 0,2 3,0 3,0 0),(2 0,4 0,4 3,2 3,2 0)),((4 0,6 0,6 3,4 3,4 0)))")
BB <- flattenSpatialPolygons(B)
SpatialPolygonsDataFrame(BB,
data.frame(soilType = paste0("Parcel_", seq_along(BB)),
row.names = getSpPPolygonsIDSlots(BB)))
})
Since January of 2014, the raster package includes a
union()
function that makes this easy-peasy:Since January 2014, the solution posted here has been completely superseded by the
raster::union()
function, demonstrated in the officially accepted answer above.This gets the "atomic" polygons formed by overlaying two different
SpatialPolygons
layers, solving Part 1 of my question.And this extracts the ids (if any) of the polygons in the two input layers overlain by each "atomic" polygon outputted by
gFragment()
above, solving part 2 of my question.Edit:
Note that this code may fail if any of your input polygons have id's named
"1"
. In that case, one workaround is to rename the id's, perhaps by doing something likeParcels <- spChFIDs(Parcels, paste0("pp", row.names(Parcels)))
.Here's my crack, this just gives a list of the pieces with the Parcel (Parcels->Soils) data. You still need to get the attributes from the Soils object, and then do a similar job with the "differences", and then vice versa (Soils->Parcels) so you can have any kinds of overlap relations.
Here's the basic idea (do this as a nested loop over parcels then over soils; I'm not sure it can be vectorized the way that the
g*
functions are written) :That should be enough to get you started. The rest is just looping while keeping track of the pieces and merging them all into one big SPDF.
An alternative approach that's more vectorized is:
This gives you more pieces than actually exist, for some reason. Then you'll have to go back and rbind them and cull the extra pieces that are
gEquals
.