22
coast<-readShapeSpatial("coastline.shp")
landc<-readShapeSpatial("landcover.shp")
ro<-readShapeSpatial("roads.shp")
bc<-gBuffer(ro,width=100)
landc$ratings=1
landc$ratings[landc$LANDUSE_ID==4]=0 

Above, I take any category that has 4 and in the new column put it as 0.

At this point, I want the column named ratings as well for the bc, where it will take 0 if it is inside the buffer and 1 if it is outside. The problem is that the bc is SpatialPolygons and it doesn't contain the attribute table.

Obviously to add a column to a SpatialPolygon object you have to convert it to a SpatialPolygonsDataFrame, but I don't know how.

I tried this:

buf_df<-as.data.frame(bc)
s_po<-SpatialPolygonsDataFrame(bc,buf_df)
s_po$ratings=0

but this error pops up:

row.names of data and Polygons IDs do not match 
gsa
  • 1,246
  • 6
  • 19
  • 29

5 Answers5

15

What do the "coast", "ro" and "bc" objects have to do with your problem? The issue may lie in that you are using "readShapeSpatial". Have you tried readOGR in rgdal? If you are reading a polygon shapefile, readOGR will result in a SpatialPolygonsDataFrame object.

If you in fact, do have a SpatialPolygons object and want to coerce into SpatialPolygonsDataFrame the specified dataframe will need to have its rownames match the polygon ID's in the polygons slot. Here is a quick example.

library(sp)

# create some SpatialPolygons with ID's "2" and "3"
( p <- SpatialPolygons(list(Polygons(list(Polygon(cbind(c(2,4,4,1,2),c(2,3,5,4,2)))), "2"),
     Polygons(list(Polygon(cbind(c(5,4,2,5),c(2,3,2,2)))), "3"))) )
class(p)    

# Create a dataframe and display default rownames
( p.df <- data.frame( ID=1:length(p)) ) 
rownames(p.df)

# Try to coerce to SpatialPolygonsDataFrame (will throw error)
p <- SpatialPolygonsDataFrame(p, p.df) 

# Extract polygon ID's
( pid <- sapply(slot(p, "polygons"), function(x) slot(x, "ID")) )

# Create dataframe with correct rownames
( p.df <- data.frame( ID=1:length(p), row.names = pid) )    

# Try coersion again and check class
 p <- SpatialPolygonsDataFrame(p, p.df)
 class(p) 

# Now we can add a column
p@data$ratings <- 1:2 

# Or modify an existing one
p[p$ID < 2 ,] <- 5
Jeffrey Evans
  • 31,750
  • 2
  • 47
  • 94
  • i showed the rest of the code like the "coast","ro" and "bc" to help you get the idea of what i am trying to do in general. i need to get a more to the point answer of what i'm asking and it would be wise to use my own variables in order to be more understandable.Plus, what is the deal with the readOGR and the readShapeSpatial? The "bc" contains the roads buffer which is the spatialpolygons object that i need to add the new column to, thus i have to convert it to a spatialPolygonsDataFrame first. – gsa Apr 05 '15 at 15:35
11

Try:

#Code taken from the question:
s_po <- SpatialPolygonsDataFrame(bc, buf_df, match.ID = F) 

match.ID avoids the requirement of rownames for match polygons ID

Jot eN
  • 972
  • 8
  • 21
Fabián
  • 111
  • 1
  • 2
  • 4
    Don't be contrary, it is not appreciated! The reason "complicated" answers were provided is because in 2015, when the question was addressed, the match.ID argument was not available in the coersion method. – Jeffrey Evans Feb 20 '18 at 00:28
7

It is quite simple:

library("rgdal")
polygons <- readOGR('path_to/file.shp',
                      layer = 'file')
class(polygons)
>[1] "SpatialPolygonsDataFrame"
>attr(,"package")
>[1] "sp"

poly_df <- as.data.frame(polygons)
# do some staff with "poly_df" that doesn't support SpatialPolygonsDataFrame
# then convert it to SPDF back again
s_poly <- SpatialPolygonsDataFrame(polygons, poly_df)
# add new column to SPDF:
s_poly$new_column <- "some data" 

When the Error: "row.names of data and Polygons IDs do not match" arise this solution seems to be helpful: rename IDs of the dataframe to match IDs of the polygons:

newdata <- data.frame(whatever you want in here)
row.names(newdata) <- (however the new polygons are labeled)
polygons <- SpatialPolygonsDataFrame(polygons, newdata)
SS_Rebelious
  • 5,621
  • 3
  • 27
  • 62
  • 2
    Not sure on a couple steps here. This coercion is dubious: as.data.frame(polygons). You example is coercing the object into the same class. Additionally, in a real example you would throw an error because the rownames of the dataframe would not match the ID in the polygon slot(s). You need to pull the polygon ID's and assign them to the rownames before coercion. – Jeffrey Evans Apr 04 '15 at 15:33
  • @JeffreyEvans, this is a copy-paste from the working code. No errors, everything is working. Just read some documentation on how SpatialPolygonsDataFrame is created. – SS_Rebelious Apr 04 '15 at 21:39
  • 4
    But, since you use readOGR in your example, you are starting with a SpatialPolygonsDataFrame and the dataframe you subset already has the correct rownames because you pulled it from the original sp object. It is a strawman example. – Jeffrey Evans Apr 05 '15 at 00:41
  • @JeffreyEvans, I edited my answer to clarify the meaning. – SS_Rebelious Apr 05 '15 at 12:07
  • see my last edit in the main post and tell me what i did wrong cause i think i made it according to your comment but it gives error. Use my own variables to make it more comprehensable. Thanks – gsa Apr 05 '15 at 12:11
  • @gsa, firstly what is the class of bc, form which package it comes from? I just can't reproduce your error for now. Though I added a workaround I found for the error you get. – SS_Rebelious Apr 05 '15 at 12:45
  • The "bc" is class "SpatialPolygons" from the "sp" package. it contains the buffer of the roads and i want to add into it a column called "ratings" with all the values being zero. Just like i did with the "landc" in main post."landc" is SpatialPolygonsDataFrame,"bc" has to be too i suppose, in order to achieve what i want. – gsa Apr 05 '15 at 18:02
1

I find the following solution generally works.

First, create an empty dataframe with ID as field:

df <- data.frame(ID=character(), stringsAsFactors=FALSE )

Then get the IDs of spatial polygon bc:

for (i in bc@polygons ) { df <- rbind(df, data.frame(ID=i@ID, stringsAsFactors=FALSE))  }
# and set rowname=ID
row.names(df) <- df$ID

Then use df as the second argument to the spatial dataframe conversion function:

spatial_df <- SpatialPolygonsDataFrame(bc, df)

As df and spatial_df are dataframe objects, columns can be easily added

user55570
  • 156
  • 5
0

I used this solution from Roger B. https://stat.ethz.ch/pipermail/r-help/2005-December/085175.html

yourDF <- as.data.frame(yourPolygon)

set rowname=ID from polygon

row.names(yourDF) <- sapply(slot(yourPolygon, "polygons"), function(x) slot(x, "ID"))

Then creates back your Spatial Data Frame with:

yourNewPolygon<- SpatialPolygonsDataFrame(yourPolygon, yourDF)

Test any operation on it like

yourNewPolygon<- elide(yourNewPolygon, rotate=-50)