Extract raster by a list of SpatialPolygonsDataFrame objects in R - r

I am trying to extract summed raster cell values from a single big file for various SpatialPolygonsDataFrames (SPDF) objects in R stored in a list, then add the extracted values to the SPDF objects attribute tables. I would like to iterate this process, and have no idea how to do so. I have found an efficient solution for multiple polygons stored in a single SPDF object (see: https://gis.stackexchange.com/questions/130522/increasing-speed-of-crop-mask-extract-raster-by-many-polygons-in-r), but do not know how to apply the crop>mask>extract procedure to a LIST of SPDF objects, each containing multiple polygons. Here is a reproducible example:
library(maptools) ## For wrld_simpl
library(raster)
## Example SpatialPolygonsDataFrame
data(wrld_simpl) #polygon of world countries
bound <- wrld_simpl[1:25,] #country subset 1
bound2 <- wrld_simpl[26:36,] #subset 2
## Example RasterLayer
c <- raster(nrow=2e3, ncol=2e3, crs=proj4string(wrld_simpl), xmn=-180,
xmx=180, ymn=-90, ymx=90)
c[] <- 1:length(c)
#plot, so you can see it
plot(c)
plot(bound, add=TRUE)
plot(bound2, add=TRUE, col=3)
#make list of two SPDF objects
boundl<-list()
boundl[[1]]<-bound1
boundl[[2]]<-bound2
#confirm creation of SPDF list
boundl
The following is what I would like to run for the entire list, in a forloop format. For a single SPDF from the list, the following series of functions seem to work:
clip1 <- crop(c, extent(boundl[[1]])) #crops the raster to the extent of the polygon, I do this first because it speeds the mask up
clip2 <- mask(clip1, boundl[[1]]) #crops the raster to the polygon boundary
extract_clip <- extract(clip2, boundl[[1]], fun=sum)
#add column + extracted raster values to polygon dataframe
boundl[[1]]#data["newcolumn"] = extract_clip
But when I try to isolate the first function for the SPDF list (raster::crop), it does not return a raster object:
crop1 <- crop(c, extent(boundl[[1]])) #correctly returns object class 'RasterLayer'
cropl <- lapply(boundl, crop, c, extent(boundl)) #incorrectly returns objects of class 'SpatialPolygonsDataFrame'
When I try to isolate the mask function for the SPDF list (raster::mask), it returns an error:
maskl <- lapply(boundl, mask, c)
#Error in (function (classes, fdef, mtable) : unable to find an inherited method for function ‘mask’ for signature ‘"SpatialPolygonsDataFrame", "RasterLayer"’
I would like to correct these errors, and efficiently iterate the entire procedure within a single loop (i.e., crop>mask>extract>add extracted values to SPDF attribute tables. I am really new to R and don't know where to go from here. Please help!

One approach is to take what is working and simply put the desired "crop -> mask -> extract -> add" into a for loop:
for(i in seq_along(boundl)) {
clip1 <- crop(c, extent(boundl[[i]]))
clip2 <- mask(clip1, boundl[[i]])
extract_clip <- extract(clip2, boundl[[i]], fun=sum)
boundl[[i]]#data["newcolumn"] <- extract_clip
}
One can speed-up the loop with parallel execution, e.g., with the R package foreach. Conversely, the speed gain of using lapply() instead of the for loop will be small.
Why the error occurs:
cropl <- lapply(boundl, crop, c, extent(boundl))
applies the function crop() to each element of the list boundl. The performed operation is
tmp <- crop(boundl[[1]], c)
## test if equal to first element
all.equal(cropl[[1]], tmp)
[1] TRUE
To get the desired result use
cropl <- lapply(boundl, function(x, c) crop(c, extent(x)), c=c)
## test if the first element is as expected
all.equal(cropl[[1]], crop(c, extent(boundl[[1]])))
[1] TRUE
Note:
Using c to denote an R object is a bade choice, because it can be easily confused with c().

Related

How to create a list of several rasters from a crop and mask (cut) from shapefile in R

I have 14 raster files from each year of land use. I would like to crop these rasters using my shapefile area of interest. With these cut rasters I would like to create a list where each element represents each year cut by the shapefile.
I tried in a very simple and fast way using the for, crop and mask commands, but without success.
my data exemplo:
raster and shape files : https://drive.google.com/file/d/1IOGWZ3_ckKj3UoZVd7ikR5l9n04xd6He/view?usp=sharing
my code
library(raster)
library(rgdal)
library(sf)
setwd('dir')
raster_solo_2005<-raster('utm-50-2005.tif')
raster_solo_2006<-raster('utm-50-2006.tif')
raster_solo_2007<-raster('utm-50-2007.tif')
raster_solo_2008<-raster('utm-50-2008.tif')
raster_solo_2009<-raster('utm-50-2009.tif')
raster_solo_2010<-raster('utm-50-2010.tif')
raster_solo_2011<-raster('utm-50-2011.tif')
raster_solo_2012<-raster('utm-50-2012.tif')
raster_solo_2013<-raster('utm-50-2013.tif')
raster_solo_2014<-raster('utm-50-2014.tif')
raster_solo_2015<-raster('utm-50-2015.tif')
raster_solo_2016<-raster('utm-50-2016.tif')
raster_solo_2017<-raster('utm-50-2017.tif')
raster_solo_2018<-raster('utm-50-2018.tif')
#list raster
list_raster<-as.list(raster_solo_2005, raster_solo_2006, raster_solo_2007, raster_solo_2008, raster_solo_2009, raster_solo_2010, raster_solo_2011, raster_solo_2012, raster_solo_2013, raster_solo_2014, raster_solo_2015, raster_solo_2016, raster_solo_2017, raster_solo_2018)
#shapefile
shp_area<-sf::st_read('500m_utm.shp')
#creat list empity
list_raster_cut<-list()
#loop operation
for(i in 1:10){
# crop e mask
list_raster_cut[[i]] <- list_raster %>%
raster::crop(shp_area)%>%
raster::mask(shp_area)
}
Update: exit error
Error in h(simpleError(msg, call)) : error evaluating argument 'x' when selecting method for function 'mask': 'unable to find an inherited method for function 'crop' for signature '"list" , "sf"''
The problem is that you need to also subset the list_raster object inside the for loop.
list_raster_cut[[i]] <- list_raster[[i]] %>%
raster::crop(shp_area)%>%
raster::mask(shp_area)

How to Loop over Rasters to convert them to data.frames

I have some rasters that I would like to transform to data frames. I can do it manually one by one but it is ineffcient. When I try to make a loop (using a list or vector with names) the code doesn't work and R error says " Error in as.data.frame.default(x[[i]], optional = TRUE) : cannot coerce class ‘structure("RasterLayer", package = "raster")’ to a data.frame"
I have tried to make it using the function assign() but it doesn't work either. When using a vector of names I can only get R to make a dataframe of one single observation containing the name of the vector
When I do it one by one, R actually makes what I want. My code for one raster is just
#"a" is the name of the raster
r_1 <- as.data.frame(a, xy=TRUE, na.rm=TRUE, centroids=TRUE)
I have tried several things to male a loop but all have failed. First, I tried by creating a vector and looping with the function assign()
# "a" and "b" are the names of my rasters
o2 <- c("a","b")
for(i in 1:length(o2)){
nam <- substr(o2[i],1,nchar(o2))
assign(nam,as.data.frame(o2[i], xy=TRUE, na.rm=TRUE, centroids=TRUE))
}
But this only creates a dataframe named a1 with one observation "a1" and one variable.
I have tried to make a list too
o4 <- list(a,b)
for(i in 1:length(o4)){
nam <- substr(o4[i],1,nchar(ola4))
r_i <- as.data.frame(o4[i], xy=TRUE, na.rm=TRUE, centroids=TRUE)
}
The error this time says: " Error in as.data.frame.default(x[[i]], optional = TRUE) : cannot coerce class ‘structure("RasterLayer", package = "raster")’ to a data.frame"
I expect to have a data frame with three columns and as much rows as cells in my raster. The columns should be the latitude and longitude of the centroid of each cell and a column with the information each cell. I don't see any mistake in my code, maybe someone can help me.
I created the rasters myself using different shapefiles. I have more than 40 rasters with the following characteristics: witdth 8806, height: 10389, origin: -77.6699, 4.94778, pixel size: 0,001041666, SRC: EPSG:4326 - WGS 84 - Geographic. As I said, I created the rasters myself and all of them have those same characteristics.
When asking a question like this, always include some example data (normally not your data). Here are use three (identical) raster files
f <- system.file("external/test.grd", package="raster")
ff <- c(f,f,f)
Now use lists to accomplish what you want.
r <- lapply(ff, raster)
x <- lapply(r, function(i) as.data.frame(i, xy=TRUE, na.rm=TRUE))
Never use assign
Instead of a loop you can use apply :
s=c(raster1,raster2,raster3)
lapply(s, as.data.frame)

Convert SpatialCollections to SpatialPolygonsDataFrame in R

I am struggling to convert an object of class SpatialCollections to a SpatialPolygonsDataFrame object.
My input files are both shapefiles and SpatialPolygonsDataFrame objects. They can be accessed here.
I do an intersection of both objects:
SPDF_A <- shapefile("SPDF_A")
SPDF_B <- shapefile("SPDF_B")
intersection <- gIntersection(gBuffer(SPDF_A, width=0), gBuffer(SPDF_B, width=0))
The result is:
> intersection
class : SpatialCollections
Setting gBuffer(... , byid=T) or gBuffer(... , byid=F) seems to make no difference.
I use gIntersection and gBuffer(... , width=0) insetead of intersect in order to avoid geometrical problems (Self-intersection).
This is part of a larger loop. I need to get the intersection as SpatialPolygonsDataFrame because it will be saved as shp file in a following step.
writeOGR(intersection, ".", layer=paste0("Int_SPDF_A-SPDF_B"), driver="ESRI Shapefile")
This is not possible from a SpatialCollections object. In order to convert this to a SpatialPolygonsDataFrame I tried:
intersection <- as(intersection ,"SpatialPolygonsDataFrame")
intersection <- SpatialPolygonsDataFrame(intersection)
intersection <- readOGR(intersection, layer = "intersection")
Nothing works. Does anybody have a solution? Thanks a lot!
First of all, according to the documentation SpatialCollections is kind of a container format that can "hold SpatialPoints, SpatialLines, SpatialRings, and SpatialPolygons (without attributes)". If you need the data frame part of your SpatialPolygonsDataFrame ("attribute table" in GIS language), you'll have to work around that somehow. If, on the other hand, you're only interested in the spatial information (the polygons without the data attached to them) try the following:
str(intersection, max.level = 3)
suggests that the #polyobj is nothing but a SpatialPolygons object. Hence
mySpoly <- intersection#polyobj
should do the trick and
class(mySpoly)
suggests that we indeed now have a SpatialPolygons.
You need to convert that to a SpatialPolygonsDataFrame before exporting:
mySpolyData <- as(mySpoly, "SpatialPolygonsDataFrame")
writeOGR(mySpolyData, ".", layer=paste0("Int_SPDF_A-SPDF_B"), driver="ESRI Shapefile")

Spatial Polygon sampling error in R

I have a shape file of 200 counties. How should I sample in order to subset counties from the existing 200? I have tried using the below R code:
library(maptools)
TXcounties <- readShapePoly("C:/Users/Rvg296/Downloads/TXCountiesShapeFiles/TXCounties.shp")
idx <- sample(1:250, 25, replace = FALSE)
df.TXcounties <- as.data.frame(TXcounties)
SpatialPolygonsDataFrame(idx, df.TXcounties).
But this is throwing an error like:
Error in SpatialPolygonsDataFrame(idx, df.TXcounties) : trying to get slot "polygons" from an object of a basic class ("integer") with no slots
The problem is that you are using idx, an integer vector, as the first argument for SpatialPolygonsDataFrame(), but this function needs a spatial polygons object as its first argument. In any case, you should be able to do the whole thing a lot more easily with something like this:
result <- TXcounties[idx,]

stack{raster} of n Raster Layers

I have a question about stack() Raster Layers.
Usually I stack() Raster Layers like that:
stack(RasterLayer1,RasterLayer2,RasterLayer3) # e.g. for 3 Layers
My Question is, how can I stack() Raster Layers without typing in every Raster Layer?
For example: n is the amount of Raster Layers (e.g. 12), all named band.
I created n-Raster Layers and now I want to stack all without typing n-times the Name of the Raster Layers. So instead of typing:
stack(band1,band2,band3,band4,band5,band6,band7,band8,band9,band10,band11,band12)
I want to short that by stack(band[n]), but that doesn't work.
And if I create a list of all bands, I can't stack that list, because they don't appear in my Working Directory because I just created them.
Can anyone help me, please?
If your data is in a directory, you can use a search pattern (for example: *.tif, *.grd,...) and store it in a variable.
bands <- list.files(path=".",pattern="*.tif",full.names=TRUE,recursive=TRUE)
now assume that your data is called:
band_01.tif
band_02.tif
band_03.tif
band_04.tif
band_05.tif
band_06.tif
band_07.tif
then you can stack for example:
data_stack <- stack(bands) #stack all data
data_stack <- stack(bands[1:3]) #stack 1,2 and 3 data
data_stack <- stack(bands[c(1,3,5,7)])
I would recommend not to save them under separate variables like band1,band2,... but instead store them in a list. Here an example:
#Create empty rasters
ras1<- raster()
ras2<- raster()
#Initialise and append to list
list_ras <- list()
list_ras[[1]] <- ras1
list_ras[[2]] <- ras2
#Stack single bands
ras_stack <- stack(list_ras[[1]], list_ras[[2]])
#Stack all bands
ras_stack <- stack(list_ras)
Here is an other approach using mget:
# Generate some data
library(raster)
r <- raster()
r[] <- runif(ncell(r))
for (i in 1:10) assign(paste0("r", i), r)
# create a stack
stack(mget(ls(pattern = "^r.+")))

Resources