Write categorical RasterStack to disk with levels/attributes - r

I have a rasterStack (or RasterBrick) with categorical layers. When I write it to disk, and then load it again, I lose the attributes (e.g. the levels associated to my layers).
I would like to be able to save the attributes of the layers in my .grd file. Is it possible ?
MWE:
library(raster)
list_r=list()
for(i in 1:3){
r=raster(ncol=10, nrow=10, xmx=-80, xmn=-150, ymn=20, ymx=60)
values(r) <- sample(1:5,size = ncell(r),replace = T)
r <- ratify(r)
rat <- levels(r)[[1]]
rat$name <- c('A', 'B', 'C','D','E')
levels(r) <- rat
list_r[[i]]=r
}
final_r=stack(list_r)
#The levels are included in the rasterStack
levels(final_r)
writeRaster(final_r,filename = "save.grd")
loaded_final_r=stack("save.grd")
#No levels remaining after loading
levels(loaded_final_r)

That is not working, but you can save them for a single layer like this
x <- writeRaster(final_r[[1]],filename = "save.grd", overwrite=T)
x
#class : RasterLayer
#dimensions : 10, 10, 100 (nrow, ncol, ncell)
#resolution : 7, 4 (x, y)
#extent : -150, -80, 20, 60 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +no_defs
#source : save.grd
#names : layer.1
#values : 1, 5 (min, max)
#attributes :
# ID name
# 1 A
# 2 B
# 3 C
# 4 D
# 5 E

Related

Subset R rasterstack based on difference in raster layers grid cell numbers

I want to create subsets of raster stacks and write them as new stacks when the difference between the previous layer and the next layer is all NA. I.e., starting from layer 1, I want to create a subset of raster stacks until there are no-overlapping pixels between the previous and next layers (i.e., the difference between the two layers is all NA) So I want is; starting from layer 1, retain all the layers that have at least 1 common pixel between the previous and next layer, write them as a 1 stack, and move to the next. Below are a sample data and unsuccessful for-loop. In this example, I want to retain layers 1:8, name and write them and start again from layer 9 and so on.
r <- raster(ncol=5, nrow=5)
set.seed(0)
#create raster layers with some values
s <- stack(lapply(1:8, function(i) setValues(r, runif(ncell(r)))))
s1<-extend(s,c(-500,100,-400,100))
#to recreate the condition I am looking for, create 2 layers with `NA` vlaues
s2 <- stack(lapply(1:2, function(i) setValues(r, runif(ncell(r)))))
s1e<-extend(s2,c(-500,100,-400,100))
s1e[]<-NA
#Stack the layers
r_stk<-stack(s1,s1e)
plot(r_stk)
#here is the sample code showing what i am expecting here but could not get
required_rst_lst<-list() # sample list of raster layers with overlapping pixels I am hoping to create
for ( i in 1: nlayers(r_stk))
# i<-1
lr1<-subset(r_stk,i)
lr1
lr2<-subset(r_stk,i+1)
lr2
diff_lr<-lr1-lr2
plot(diff_lr)
if ((sum(!is.na(getValues(diff_lr)))) ==0)) #??
required_rst_lst[[i]] #?? I want layers 1: 8 in this list
#because the difference in these layers in not NA
Something like this may work for you.
Your example data
library(raster)
r <- raster(ncol=5, nrow=5)
set.seed(0)
s <- stack(lapply(1:8, function(i) setValues(r, runif(ncell(r)))))
s1 <- extend(s,c(-500,100,-400,100))
s2 <- stack(lapply(1:2, function(i) setValues(r, runif(ncell(r)))))
s1e <- extend(s2,c(-500,100,-400,100))
values(s1e) <- NA
r_stk <- stack(s1,s1e)
Solution:
out <- lst <- list()
nc <- ncell(r_stk)
for (i in 1:nlayers(r_stk)) {
if (i==1) {
j <- 1
s <- r_stk[[i]]
} else {
s <- s + r_stk[[i]]
}
if (freq(s, value=NA) == nc) {
ii <- max(j, i-1)
out <- c(out, r_stk[[j:ii]])
s <- r_stk[[i]]
j <- i
}
}
out <- c(out, r_stk[[j:i]])
out
#[[1]]
#class : RasterStack
#dimensions : 14, 9, 126, 8 (nrow, ncol, ncell, nlayers)
#resolution : 72, 36 (x, y)
#extent : -468, 180, -414, 90 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +no_defs
#names : layer.1.1, layer.2.1, layer.3, layer.4, layer.5, layer.6, layer.7, layer.8
#min values : 0.06178627, 0.01339033, 0.07067905, 0.05893438, 0.01307758, 0.03554058, 0.06380848, 0.10087313
#max values : 0.9919061, 0.8696908, 0.9128759, 0.9606180, 0.9926841, 0.9850952, 0.8950941, 0.9437248
#
#[[2]]
#class : RasterLayer
#dimensions : 14, 9, 126 (nrow, ncol, ncell)
#resolution : 72, 36 (x, y)
#extent : -468, 180, -414, 90 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +no_defs
#source : memory
#names : layer.1.2
#values : NA, NA (min, max)
#
#[[3]]
#class : RasterLayer
#dimensions : 14, 9, 126 (nrow, ncol, ncell)
#resolution : 72, 36 (x, y)
#extent : -468, 180, -414, 90 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +no_defs
#source : memory
#names : layer.2.2
#values : NA, NA (min, max)

Extract NetCDF variable that have more than 4 dimension using brick function in raster package

I want to extract a variable called NVEL from the netCDF file which has five dimensions (i, j, tile, k, time)
where i is longitude, j is latitude, k is the level of depths
I want to extract NVEL(i, j, tile=3, k=1st level, time)
the input file can be downloaded from here https://drive.google.com/file/d/12NQp_uLr_IZLLU6Fzr555gKGGJlrRE4H/view?usp=sharing
NVEL<- brick("NVEL_1992_01.nc", varname= "NVEL", lvar=1, nl=1)
NVEL <- NVEL[[which(getZ(NVEL) == 3)]]
This does not work.
How to deal with a variable of 5 dimensions?
I see that this returns 50 (k) * 13 (tiles) * 1 (time) = 650 layers
library(terra)
f <- "NVEL_1992_01.nc"
x <- rast(f)
x
#class : SpatRaster
#dimensions : 90, 90, 650 (nrow, ncol, nlyr)
#resolution : 1, 1 (x, y)
#extent : -0.5, 89.5, -0.5, 89.5 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=longlat +datum=WGS84 +no_defs
#data source : NVEL_1992_01.nc
#names : NVE_1, NVE_2, NVE_3, NVE_4, NVE_5, NVE_6, ...
The order is k-wise (and tile-wise within tiles). See (the rather lengthy) output from
terra::describe(f)
You can extract that information like this:
d <- describe(f, print=FALSE)
d <- unlist(strsplit(d, "\n"))
i <- grep("NETCDF_DIM_k=", d)
j <- grep("NETCDF_DIM_tile=", d)
k <- sapply(strsplit(d[i], "="), function(x) x[2])
tile <- sapply(strsplit(d[j], "="), function(x) x[2])
kt <- paste0("k", k, "_tile", tile)
names(x) <- kt
x
#class : SpatRaster
#dimensions : 90, 90, 650 (nrow, ncol, nlyr)
#resolution : 1, 1 (x, y)
#extent : -0.5, 89.5, -0.5, 89.5 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=longlat +datum=WGS84 +no_defs
#data source : NVEL_1992_01.nc
#names : k0_tile0, k0_tile1, k0_tile2, k0_tile3, k0_tile4, k0_tile5, ...
This should happen automatigically in a future version. You can continue with terra (very similar to raster) or take the data back to a RasterBrick by doing
b <- brick(x*1)
(multiplying to get the values out of the file)

Band math with calc and / or overlay returning single-valued images

When I try to run band math the result is always an image of a color and the values min and max very different from the one predicted.
I did not find any question here that showed this problem.
I worked out this way
r.stack <- stack("path to raster file"))
I use resampling instead of crop to cut out the white edges that were in the original images
prj <- "+proj=utm +zone=23 +south +datum=WGS84 +units=m"
r <- raster(res=11.47, ext=extent(c(301496, 323919, 9888968, 9913982)), crs=prj, vals=NA
r.stack <- resample(r.stack, r)
After that the images have this configuration:
> class : RasterBrick
> dimensions : 2181, 1955, 4263855, 4 (nrow, ncol, ncell, nlayers)
> resolution : 11.47, 11.47 (x, y)
> extent : 301496, 323919.8, 9888966, 9913982 (xmin, xmax, ymin, ymax)
>coord. ref. : +proj=utm +zone=23 +south +datum=WGS84 +units=m +ellps=WGS84 +towgs84=0,0,0
>data source : in memory
>names : l.1, l.2, l.3, l.4
>min values : -36.12217, -45.12768, -46.30455, -35.26328
>max values : 10.567671, 4.050200, 3.878345, 11.613799
and than use the function below for calc
f <- function(x){
(x[[2]])/(x[[1]])
}
s <- r.stack[[c(1,2)]]
r2 <- calc(s, f)
and I also run overlay whit the fun
f <- function(x,y){
y/x
}
r2 <- overlay(r.stack[[1]], r.stack[[2]], fun= f)
Any of the methods result in a image of one value
Am I missing some steps?
Here is your code with some example data (without that it is hard to answer questions). I have simplified one function, a bit, but the results are the same.
library(raster)
b <- brick(system.file("external/rlogo.grd", package="raster"))
b <- b/10 + 1
f <- function(x){ x[2]/ x[1] }
s <- b[[c(1,2)]]
r1 <- calc(s, f)
f <- function(x,y){ y / x }
r2 <- overlay(b[[1]], b[[2]], fun= f)
Or simply
r3 <- b[[2]] / b[[1]]
r3
#class : RasterLayer
#dimensions : 77, 101, 7777 (nrow, ncol, ncell)
#resolution : 1, 1 (x, y)
#extent : 0, 101, 0, 77 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=merc +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#data source : in memory
#names : layer
#values : 0.7692308, 1.7 (min, max)
r1 and r2 are the same.
The reason that you get a "single color" is because most values are near 1, but there are a few big outliers; probably because of a division by a number between -1 and 1? This might illustrate it:
q <- quantile(r3, c(0.1, 0.9))
d <- clamp(r3, q[1], q[2])
plot(d)
And look at the extremes
i <- which.max(r3)
b[i][,2:1]

How to automatically convert many fields of a polygon shapefile to raster in R

I have a shapefile representing Thiessen polygons.
Each polygon is associated with many values of a table.
thiessen <- readOGR(dsn = getwd(), layer = poly)
OGR data source with driver: ESRI Shapefile
Source: ".../raingauges/shp", layer: "thiessen_pol"
with 10 features
It has 5 fields
head(thiessen)
est est_name p001 p002 p003
0 2 borges 1 8 2
1 0 e018 2 4 3
2 5 starosa 5 15 1
3 6 delfim 4 2 2
4 1 e087 1 1 3
5 3 e010 0 1 0
The columns 'est' and 'est_name' are related to the ID and name of the rain gauges. The following columns are important to me and represent precipitation values on day 1, 2, and so on (in the exemple I kept just three days, but actually, I have 8 years of daily precipitation data).
I need to convert the polygons to raster, but one raster for each field (column p001, p002, and so on) of the table.
There is a simple way to convert polygons to raster with the function rasterize in R.
r_p001 <- rasterize(thiessen, r, field = thiessen$p001)
plot(r_p001)
writeRaster(r_p001, filename=".../raingauges/shp/r_p001.tif")
The problem is that I need to set manually the field (column) of the table with the polygon values to be converted to raster. As I have about 2900 days (2900 columns with precipitation values for each rain gauge), it is impossible to do manually.
The documentation does not help to clarify how to automate this process and I did not find anything on the internet to help me.
Does anyone know how to automatically convert each field to raster and save as tif format?
Here is an approach:
Example data
library(raster)
r <- raster(ncols=36, nrows=18)
p1 <- rbind(c(-180,-20), c(-140,55), c(10, 0), c(-140,-60), c(-180,-20))
hole <- rbind(c(-150,-20), c(-100,-10), c(-110,20), c(-150,-20))
p1 <- list(p1, hole)
p2 <- rbind(c(-10,0), c(140,60), c(160,0), c(140,-55), c(-10,0))
p3 <- rbind(c(-125,0), c(0,60), c(40,5), c(15,-45), c(-125,0))
att <- data.frame(id=1:3, var1=10:12, var2=c(6,9,6))
pols <- spPolygons(p1, p2, p3, attr=att)
The important thing is to have a field with a unique If your data do not have it, add it like this
pols$id <- 1:nrow(pols)
Rasterize
r <- rasterize(pols, r, field='id')
Create a layer for all other variables
x <- subs(r, data.frame(pols), by='id', which=2:ncol(pols), filename="rstr.grd")
x
#class : RasterBrick
#dimensions : 18, 36, 648, 2 (nrow, ncol, ncell, nlayers)
#resolution : 10, 10 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#data source : rstr.grd
#names : var1, var2
#min values : 10, 6
#max values : 12, 9
An alternative is to keep one layer with Raster Attribute Table, that is quicker, but depending on your purpose, perhaps a less useful method:
r <- rasterize(pols, r, field='id')
f <- as.factor(r)
v <- levels(f)[[1]]
v <- cbind(v, data.frame(pols)[,-1])
levels(f) <- v
f
#class : RasterLayer
#dimensions : 18, 36, 648 (nrow, ncol, ncell)
#resolution : 10, 10 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#data source : in memory
#names : layer
#values : 1, 3 (min, max)
#attributes :
# ID var1 var2
# 1 10 6
# 2 11 9
# 3 12 6
You can then do:
z <- deratify(f)
To get the same result as in the first example
z
#class : RasterBrick
#dimensions : 18, 36, 648, 2 (nrow, ncol, ncell, nlayers)
#resolution : 10, 10 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#data source : in memory
#names : var1, var2
#min values : 10, 6
#max values : 12, 9

Error when extracting values from a rasterBrick or rasterStack

I am having trouble extracting values or a point from a multi band raster of class rasterStack or rasterBrick. 'extract' works well with the individual rasters but posts an error when applied to the rasterStack or brick.
> all.var
class : RasterBrick
dimensions : 89, 180, 16020, 34 (nrow, ncol, ncell, nlayers)
resolution : 2, 2 (x, y)
extent : -179, 181, -89, 89 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : period_max, pct_signif_periodmax, pct_coi_periodmax, pct_ispos_signif, events_pos_periodmax, events_neg_periodmax, events_pos_all, events_neg_all, maxpower_pos, maxpower_neg, maxpower_events_pos, maxpower_events_neg, maxpower_pos_norm, maxpower_neg_norm, maxpower_events_pos_norm, ...
> point
Lon Lat
1 166.2790 -10.2690
2 26.9000 -33.6000
3 153.6209 -28.7001
4 113.8333 -28.6833
5 153.6335 -28.6591
6 153.5836 -28.4643
7 32.6833 -27.5333
8 32.6880 -27.5260
9 32.6880 -27.5260
10 32.6880 -27.5260
> point.extract<-extract(all.var, point, buffer=50000,na.rm=TRUE,fun=mean)
Error in apply(x, 2, fun2) : dim(X) must have a positive length
This works with individual rasters but fails with stack/brick and elicits an error only when I use a buffer argument.
Here is a working R example that illustrates the error:
library(raster)
b <- brick(nrow=89, ncol=180, nl=34, xmn=-179, xmx=181, ymn=-89, ymx=89, crs="+proj=longlat +datum=WGS84")
b[] <- 1
p <- matrix(c(166.2790,-10.2690,26.9000,-33.6000,153.6209,-28.7001,113.8333,-28.6833,153.6335,-28.6591,153.5836,-28.4643,32.6833,-27.5333,32.6880,-27.5260,32.6880,-27.5260,32.6880,-27.5260), ncol=2, byrow=TRUE)
v <- extract(b, p, buffer=50000, na.rm=TRUE, fun=mean)
That indeed gives the error you reported, probably due a bug in the raster package. Here is a work-around:
v <- extract(b, p, buffer=15000000)
# get the mean for each point (buffer) by layer
vv <- lapply(v, function(x) ifelse(is.matrix(x), colMeans(x, na.rm=TRUE), x))
# combine
do.call(rbind, vv)

Resources