I have two rasters (images), and want to overlay them using this code:
# Getting the images
library(raster)
URL1 <- "https://www.dropbox.com/s/6jjz7ou1skz88wr/raster_1.tif?dl=1"
URL2 <- "https://www.dropbox.com/s/d5xuixohjqfnfze/raster_2.tif?dl=1"
download.file(URL1, destfile=paste0(getwd(),"/", "raster_1.tif"), method="auto", mode="wb", timeout="6000")
download.file(URL2, destfile=paste0(getwd(),"/", "raster_2.tif"), method="auto", mode="wb", timeout="6000")
# Reading the images
raster_1 <- raster(list.files(pattern="raster_1.tif$"))
raster_2 <- raster(list.files(pattern="raster_2.tif$"))
# Overlaying
myFun <- function(x,y){ifelse(x==0 && y==0, 0, ifelse(x==1 && y==0, 2, ifelse(x==1 && y>0, y)))}
( res <- overlay(stack(raster_1 ,raster_2), fun = Vectorize(myFun) ) )
### R gives this error
Error in .overlayList(x, fun = fun, filename = filename, forcefun = forcefun, :
cannot use this formula, probably because it is not vectorized
I would be very grateful if anyone could help me.
Thanks.
You need a function that only uses vectorized operators. This is case where Boolean arithmetic should both succeed and be more efficient
myFun <- function(x,y){ 0*(x==0 && y==0)+
2*(x==1 && y==0)+
y*(x==1 && y>0) }
There are some edge cases that do not appear covered. Can x ever be a value other than exactly 0 or 1? Can y ever be negative?
After running my version I get:
> ( res <- overlay(stack(raster_1 ,raster_2), fun = Vectorize(myFun) ) )
class : RasterLayer
dimensions : 2958, 1642, 4857036 (nrow, ncol, ncell)
resolution : 500, 500 (x, y)
extent : -171063.8, 649936.2, 5317253, 6796253 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=utm +zone=12 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
data source : in memory
names : layer
values : 0, 14751 (min, max)
I didn't think I would need to use Vectorize around myFun,enter code here but the results seems more likely to be correct when I leave it in the call to overlay:
> Hmisc::describe(values(res))
values(res)
n missing distinct Info Mean Gmd .05 .10 .25
3222508 1634528 1502 0.727 4918 6403 0 0 0
.50 .75 .90 .95
0 13898 14082 14168
Value 0 13000 13200 13400 13600 13800 14000 14200 14400
Frequency 2089448 67 578 10515 69031 249817 523241 226628 46191
Proportion 0.648 0.000 0.000 0.003 0.021 0.078 0.162 0.070 0.014
Value 14600 14800
Frequency 6876 116
Proportion 0.002 0.000
When I took out the Vectorize step I did not get an error but I got all zeros, instead.
It is not clear what you really are trying to achieve, and there might be better solutions. In your example data, Y (raster_2) has no values of zero. That suggest that you want the values of raster_2 where raster_1 is not 0? That can be achieved like this:
m <- mask(raster_2, raster_1, maskvalue=0)
I think that 42-'s myFun has a problem in that it returns 0 when none of the conditions are true, specifically when (x == 0 & y > 0)
To make it work with overlay, replace the && with &
myFunV <- function(x,y){
0*(x==0 & y==0)+
2*(x==1 & y==0)+
y*(x==1 & y>0) }
res <- overlay(raster_1, raster_2, fun = myFunV)
(but, again, I doubt that this is good approach for your needs)
Related
I have a timeseries of geotiff files I'd like to stack in R using stars. Here's the first two:
urls <- paste0("/vsicurl/",
"https://sdsc.osn.xsede.org/bio230014-bucket01/neon4cast-drivers/",
"noaa/gefs-v12/cogs/gefs.20221201/",
c("gep01.t00z.pgrb2a.0p50.f003.tif", "gep01.t00z.pgrb2a.0p50.f006.tif"))
library(stars)
stars::read_stars(urls, along="time")
Errors with:
Error in c.stars_proxy(`3` = list(gep01.t00z.pgrb2a.0p50.f003.tif = "/vsicurl/https://sdsc.osn.xsede.org/bio230014-bucket01/neon4cast-drivers/noaa/gefs-v12/cogs/gefs.20221201/gep01.t00z.pgrb2a.0p50.f003.tif"), :
don't know how to merge arrays: please specify parameter along
Context: bands contain both time+band info
This fails because the dimensions do not match, which happens because the files have concatenated temporal information into the band names:
x<- lapply(urls, read_stars)
x
produces:
[[1]]
stars object with 3 dimensions and 1 attribute
attribute(s), summary of first 1e+05 cells:
Min. 1st Qu. Median Mean 3rd Qu. Max.
gep01.t00z.pgrb2a.0p50.f003.ti... 50026.01 98094.81 101138 98347.42 101845.2 104605.2
dimension(s):
from to offset delta refsys point
x 1 720 -180.25 0.5 Coordinate System importe... FALSE
y 1 361 90.25 -0.5 Coordinate System importe... FALSE
band 1 8 NA NA NA NA
values x/y
x NULL [x]
y NULL [y]
band PRES:surface:3 hour fcst,...,DLWRF:surface:0-3 hour ave fcst
[[2]]
stars object with 3 dimensions and 1 attribute
attribute(s), summary of first 1e+05 cells:
Min. 1st Qu. Median Mean 3rd Qu. Max.
gep01.t00z.pgrb2a.0p50.f006.ti... 50029.83 98101.83 101170.6 98337.52 101825 104588.2
dimension(s):
from to offset delta refsys point
x 1 720 -180.25 0.5 Coordinate System importe... FALSE
y 1 361 90.25 -0.5 Coordinate System importe... FALSE
band 1 8 NA NA NA NA
values x/y
x NULL [x]
y NULL [y]
band PRES:surface:6 hour fcst,...,DLWRF:surface:0-6 hour ave fcst
Note the band names would align except for the existence of the timestamp being tacked on, e.g. PRES:surface:3 hour fcst vs PRES:surface:6 hour fcst.
How can I best read in these files so that I have dimensions of x,y,band, and time in my stars object?
alternatives: terra?
How about terra? Note that terra is happy to read these files in directly, but treats this as 16 unique bands. Can I re-align that so that I have the original 8 bands along a new "time" dimension? (I recognize stars emphasizes 'spatio-temporal', maybe the such a cube is out of scope to terra?) Also note that terra for some reason mangles the timestamp in these band names:
x <- terra::rast(urls)
x
class : SpatRaster
dimensions : 361, 720, 16 (nrow, ncol, nlyr)
resolution : 0.5, 0.5 (x, y)
extent : -180.25, 179.75, -90.25, 90.25 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat Coordinate System imported from GRIB file
sources : gep01.t00z.pgrb2a.0p50.f003.tif (8 layers)
gep01.t00z.pgrb2a.0p50.f006.tif (8 layers)
names : PRES:~ fcst, TMP:2~ fcst, RH:2 ~ fcst, UGRD:~ fcst, VGRD:~ fcst, APCP:~ fcst, .
With terra it is pretty easy to make a time-series for each variable as I show below.
urls <- paste0("/vsicurl/",
"https://sdsc.osn.xsede.org/bio230014-bucket01/neon4cast-drivers/",
"noaa/gefs-v12/cogs/gefs.20221201/",
c("gep01.t00z.pgrb2a.0p50.f003.tif", "gep01.t00z.pgrb2a.0p50.f006.tif"))
library(terra)
r <- rast(urls)
Extract two variables of interest
nms <- names(r)
tmp <- r[[grep("TMP", nms)]]
rh <- r[[grep("RH", nms)]]
# set time
tm <- as.POSIXct("2022-12-01", tz="GMT") + c(3,6) * 3600
time(rh) <- tm
time(tmp) <- tm
And you could combine them into a SpatRasterDatset like this:
s <- sds(list(tmp=tmp, rh=rh))
An alternative path to get to the same point would be to start with a SpatRasterDataset and subset it.
sd <- sds(urls)
nl <- 1:length(sd)
nms <- names(sd[1])
tmp2 <- rast(sd[nl, grep("TMP", nms)])
time(tmp2) <- tm
rh2 <- rast(sd[nl, grep("RH", nms)])
time(rh2) <- tm
I made the subsetting work a little nicer in terra version 1.7-5
urls <- paste0("/vsicurl/",
"https://sdsc.osn.xsede.org/bio230014-bucket01/neon4cast-drivers/",
"noaa/gefs-v12/cogs/gefs.20221201/", c("gep01.t00z.pgrb2a.0p50.f003.tif", "gep01.t00z.pgrb2a.0p50.f006.tif"))
library(terra)
#terra 1.7.5
sd <- sds(urls)
tmp <- sd[,2]
tmp
#class : SpatRaster
#dimensions : 361, 720, 2 (nrow, ncol, nlyr)
#resolution : 0.5, 0.5 (x, y)
#extent : -180.25, 179.75, -90.25, 90.25 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat Coordinate System imported from GRIB file
#sources : gep01.t00z.pgrb2a.0p50.f003.tif
# gep01.t00z.pgrb2a.0p50.f006.tif
#names : TMP:2 m above g~Temperature [C], TMP:2 m above g~Temperature [C]
#unit : C, C
#time : 2022-12-01 03:00:00 to 2022-12-01 06:00:00 UTC
As for the layer names containing the forecast time, that is just because that is what is in the tif metadata. It looks like that was a decision made when they were created from the original GRIB files.
The latitude extent going beyond the north and south poles is an interesting feature of this dataset.
Just wanted to share some additional possible solutions for comparison. With larger numbers of files some of these differences become more relevant. this expands a bit beyond my original question.
terra
Prof Hijmans gives a very nice solution in terra. He also asked about the original upstream sources, which I didn't explain properly -- these are originally GRIB files for NOAA GEFS forecast.
Notably, we can work directly from the GRIB files. GEFS is a 35-day forecast, so let's try going more than 6 hrs into the future:
library(terra)
# original GRIB sources, AWS mirror
gribs <- paste0("/vsicurl/https://noaa-gefs-pds.s3.amazonaws.com/gefs.20220314/00/atmos/pgrb2ap5/geavg.t00z.pgrb2a.0p50.f",
stringr::str_pad(seq(3,240,by=3), 3, pad="0"))
bench::bench_time({
cube <- terra::sds(gribs)
})
cube[1,63] |> plot()
very nice!
gdalcubes
gdalcubes is another package that can also leverage the gdal virtual filesystem when working with these large-ish remote files. It also lets us define an abstract cube at potentially a different resolution in space & time than the original sources (averaging or interpolating). lazy operations mean this may run a bit faster(?)
library(gdalcubes)
date <- as.Date("2023-01-26")
date_time = date + lubridate::hours(seq(3,240,by=3))
# USA box
v <- cube_view(srs = "EPSG:4326",
extent = list(left = -125, right = -66,top = 49, bottom = 25,
t0= as.character(min(date_time)), t1=as.character(max(date_time))),
dx = 0.5, dy = 0.5, dt = "PT3H")
gribs <- paste0("/vsicurl/https://noaa-gefs-pds.s3.amazonaws.com/gefs.20220314/00/atmos/pgrb2ap5/geavg.t00z.pgrb2a.0p50.f",
stringr::str_pad(seq(3,240,by=3), 3, pad="0"))
bench::bench_time({
cube <- gdalcubes::create_image_collection(gribs, date_time = date_time)
})
bench::bench_time({
raster_cube(cube, v) |>
select_bands("band63") |> # tempearture
animate(col = viridisLite::viridis, nbreaks=50, fps=10, save_as = "temp.gif")
})
stars
didn't translate a full stars example, but here at least is the band name correction; a bit more cumbersome than the examples above.
urls <- paste0("/vsicurl/",
"https://sdsc.osn.xsede.org/bio230014-bucket01/neon4cast-drivers/",
"noaa/gefs-v12/cogs/gefs.20221201/",
c("gep01.t00z.pgrb2a.0p50.f003.tif", "gep01.t00z.pgrb2a.0p50.f006.tif"))
library(stars)
#stars::read_stars(urls, along="time") # no luck!
## grab unstacked proxy object for each geotiff
x <- lapply(urls, read_stars)
# extract band-names-part
band_names <- st_get_dimension_values(x[[1]], "band") |>
stringr::str_extract("([A-Z]+):") |>
str_remove(":")
# apply corrected band-names
x1 <- lapply(x, st_set_dimensions, "band", band_names)
# at last, we can stack into a cube:
x1 <- do.call(c, c(x1, along="time"))
# and add correct date timestamps to the new time dimension
dates <- as.Date("2022-12-01") + lubridate::hours(c(3,6))
x1 <- st_set_dimensions(x1, "time", dates)
x1
I am using the Biomod2 package to run a series of species distribution models in R. One of the modelling techniques I am using is a classification tree analysis (CTA) which uses the rpart package.
The response in these models are presence/absence of a plant species and the predictor variables are contained in a rasterStack. Most of the variables in the rasterStack are continuous numeric variables with the exception of one land cover variables, geology, which is a factor. I stacked each individual rasterLayer and after, used as.factor() to convert the geology layer to a factor.
I am running into an error message when trying to predict from the CTA. The CTA model was built with a data frame in which "geology" is a factor (see below) and used the raster predict function on a rasterStack ("geology" is a factor, see below). However, running the predict function, I get an error saying I supplied a numeric instead of a factor. I have checked all possible points to see if somehow "geology" get converted back to numeric but it seems to be a factor (as it should) everywhere I look.
EDIT: Changed data to make reproduceable.
library(raster)
library(rpart)
set.seed(123)
# Create sample rasterStack
data.rast <- stack(system.file("external/rlogo.grd", package = "raster"))
# Create one layer as a factor
data.rast$geology <- as.factor(sampleInt(7, length(data.rast$red), replace = TRUE))
# Create sample presence/absence data by randomly selecting cells of raster
data <- as.data.frame(data.rast)
data <- data[sample(nrow(data), 300, replace = FALSE), ]
data$pa <- as.factor(sample(0:1, nrow(data), replace = TRUE))
names(data)[4] <- "geology"
head(data)
# red green blue geology pa
#2463 251 255 255 7 1
#1944 191 190 186 5 0
#5016 162 174 226 7 0
#5771 255 255 253 4 1
#3739 204 205 199 7 0
#5483 131 133 122 3 0
# Build CTA model using presence/absence dataframe
# Parameters set as the defaults in Biomod2 modeling options
cta <- rpart(pa ~ .,
data = data,
na.action = na.omit,
method = "class",
control = list(xval = 5,
minbucket = 5,
minsplit = 5,
cp = 0.001,
maxdepth = 25))
# Confirm classes of data before running predict function
data.frame(ctaClass = attr(terms(cta), "dataClasses")[2:5],
rasterFactor = is.factor(data.rast))
# ctaClass rasterFactor
#red numeric FALSE
#green numeric FALSE
#blue numeric FALSE
#geology factor TRUE
# Once again confirming this rasterLayer is a factor
levels(data.rast$geology)
#[[1]]
# ID VALUE
#1 1 1
#2 2 2
#3 3 3
#4 4 4
#5 5 5
#6 6 6
#7 7 7
# Run predict function on rasterStack
cta.predict <- predict(object = data.rast,
model = cta,
type = "class")
#Error: variable 'geology' was fitted with type "factor" but type "numeric" was #supplied
#In addition: Warning message:
#In model.frame.default(Terms, newdata, na.action = na.action, xlev = #attr(object, :
# variable 'geology' is not a factor
EDIT: added proof that it works with a randomForests model
library(randomForest)
rf <- randomForest(pa ~ .,
data = data,
na.action = na.omit)
rf.predict <- predict(data.rast, rf)
rf.predict
#class : RasterLayer
#dimensions : 77, 101, 7777 (nrow, ncol, ncell)
#resolution : 1, 1 (x, y)
#extent : 0, 101, 0, 77 (xmin, xmax, ymin, ymax)
#crs : +proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs
#source : memory
#names : layer
#values : 0, 1 (min, max)
#attributes :
# ID value
# 1 0
# 2 1
In this case you need to help predict a bit by providing the factor name(s) and levels
data$geology <- as.factor(data$geology)
cta.predict <- predict(data.rast, cta, type="class", factors=list(geology=levels(data$geology)))
Also note the type= in type=class, you should cannot just do class (unless you want the filename to be class.grd)
With terra this works a little better, I think (hope)
library(terra)
x <- rast(data.rast*1)
x$geology <- as.factor(x$geology)
cta.predict <- predict(x, cta, type="class")
I got myself a rasterbrick called y, which has got 14 975 time layers as its got values of daily mean geopotential heigth every day since 1.1.1979 till 31.12.2019 (14 975 days). The brick has following description:
class : RasterBrick
dimensions : 221, 121, 26741, 14975 (nrow, ncol, ncell, nlayers)
resolution : 0.25, 0.25 (x, y)
extent : 14.875, 45.125, 24.875, 80.125 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84
source : C:/Users/Adam/AppData/Local/Temp/RtmpaKZVdb/raster/r_tmp_2020-10-26_165849_53084_29346.grd
names : index_1979.01.01, index_1979.01.02, index_1979.01.03, index_1979.01.04, index_1979.01.05, index_1979.01.06, index_1979.01.07, index_1979.01.08, index_1979.01.09, index_1979.01.10, index_1979.01.11, index_1979.01.12, index_1979.01.13, index_1979.01.14, index_1979.01.15, ...
min values : 46604.85, 47328.07, 48944.12, 49320.65, 49244.67, 49516.16, 49504.01, 48959.65, 48608.90, 47603.10, 47572.72, 48564.15, 49816.92, 49078.65, 48321.72, ...
max values : 57006.81, 56968.60, 56958.67, 56976.26, 57288.55, 57535.62, 57659.48, 57581.33, 57381.65, 57052.99, 56803.95, 56854.89, 56783.50, 56739.44, 56600.52, ...
and I would like to subset this rasterbrick into 12 rasterbricks by month so that I had 1 rasterbrick for every calendar month. I tried to do that several ways but nothing worked out well. For example, I tried to substract month character from names(y), and I think its definitely the way to go but it simply does not work. Every help appreciated, thank you!
if you try with this:
# We changed the names of the layers to months:
layer_names_original <- names(y)
layer_names <- layer_names_original
layer_names_2 <- gsub('index_', '', layer_names)
layer_names_3 <- gsub('\\.', '-', layer_names_2)
layer_names_3_as_date <- as.Date(layer_names_3)
library(lubridate)
layer_names_3_in_months <- month(layer_names_3_as_date)
names(y) <- layer_names_3_in_months
# We filtered the layers of the month 'i'
i <- 1 # january
y_i <- y[[i]]
# We replaced the month names by the originals
id_match <- which(names(y)%in%i)
names(y_i) <- layer_names_original[id_match]
# Pd: Try changed brick by stack in case of problems (y <- stack(y))
You try with this:
layer_names <- names(y)
layer_names_2 <- gsub('index_', '', layer_names)
layer_names_3 <- gsub('\\.', '-', layer_names_2)
layer_names_3_as_date <- as.Date(layer_names_3)
library(lubridate)
layer_names_3_in_months <- month(layer_names_3_as_date)
# We filtered the layers of the month 'i'
i <- 1 # january
layer_names_3_in_months_i <- which(layer_names_3_in_months==i)
layer_month_i <- names(y)[layer_names_3_in_months_i]
# Filter done
y[[layer_month_i]]
# Pd: Try changed brick by stack in case of problems (y <- stack(y))
Not sure what is wrong here.
Building the model along with the example
LBsPD <- c()
for (i in 1:5000) {
FishCaught <- sample(x=c(7,4,2), size=1, prob = c(.1,.6,.3),replace = TRUE)
YellowPercent <- sample(x=c(0,.25,.35), size=1, prob = c(.25,.5,.25),replace = TRUE)
BluePercent <- 1-YellowPercent
BlueLBs <- rnorm(n=365, mean=35, sd=18)
YellowLBs <- rnorm(n=365, mean=30, sd=18)
LBsPerDay <- FishCaught * ((BluePercent * BlueLBs[BlueLBs > 20]) + (YellowPercent * YellowLBs[YellowLBs > 20]))
LBsPD[i] <- LBsPerDay
}
Keep getting the 50+ errors "Number of items to replace is not a multiple of replacement length" But in the example it is the same.
Here's a drawn-out explanation that #AndrewChisholm started.
I'll start by (1) setting the random seed, so you can repeat this in your console, and (2) stepping through the for loop's first iteration.
set.seed(42) # since this is a random process
i <- 1 # first pass in the loop
FishCaught <- sample(x=c(7,4,2), size=1, prob = c(.1,.6,.3),replace = TRUE)
YellowPercent <- sample(x=c(0,.25,.35), size=1, prob = c(.25,.5,.25),replace = TRUE)
BluePercent <- 1-YellowPercent
BlueLBs <- rnorm(n=365, mean=35, sd=18)
Now, let's look at the components of your next expression:
# FishCaught * ((BluePercent * BlueLBs[BlueLBs > 20]) +
# (YellowPercent * YellowLBs[YellowLBs > 20]))
str(BlueLBs[BlueLBs > 20])
# num [1:291] 24.8 41.5 46.4 42.3 33.1 ...
str(YellowLBs[YellowLBs > 20])
# num [1:255] 64.1 22.5 36.3 59.3 31.6 ...
It doesn't matter now that BluePercent is 1 and YellowPercent is 0, since 0*somevec is still the length of the vector, so you are effectively trying to add vectors of different sizes. What does this mean to you?
c(1,3,5,7,9) + c(1,1000,1)
# Warning in c(1, 3, 5, 7, 9) + c(1, 1000, 1) :
# longer object length is not a multiple of shorter object length
# [1] 2 1003 6 8 1009
The bigger problem here is that R does not consider this a problem: it warns you that it is suspect, but it happily "recycles" the values for you. So this is not what is causing your error.
LBsPerDay <- FishCaught * ((BluePercent * BlueLBs[BlueLBs > 20]) + (YellowPercent * YellowLBs[YellowLBs > 20]))
# Warning in (BluePercent * BlueLBs[BlueLBs > 20]) + (YellowPercent * YellowLBs[YellowLBs > :
# longer object length is not a multiple of shorter object length
str(LBsPerDay)
# num [1:291] 174 291 325 296 232 ...
This is not a syntax error, but you should treat it as a "data is now corrupt" error, because you really don't know for certain what numbers were added/multiplied to other numbers (see my previous example of with c(1,1000,1) to see that if we think things should be aligned, then those results are going to result in some incorrect logical conclusions from this process).
Here's the real problem:
LBsPD[i] <- LBsPerDay
# Warning in LBsPD[i] <- LBsPerDay :
# number of items to replace is not a multiple of replacement length
First, some clarification:
This is a Warning, not an Error. The only way I get an error with that is if I had previous set options(warn=2) (which, btw, is not a bad idea here). Warnings can often be ignored if you expect them, but in this case you should really pay attention to it and treat it as an error.
LBsPerDay is length 291, but you try trying to cram 291 numbers into one position in the vector LBsPD[i]. That is, the length of the LHS using [i] is always going to be the length of i, which is 1; whereas the length of the RHS is (in this case) 291.
Options:
I'm inferring that your BlueLBs[BlueLBs > 20] might be a filter so that fish caught below 20 (pounds? kilos? grams?) will not be "caught". In that case, let's just replace those under 20 with 0 ... but please check me on this logic ... a blue/yellow that is below 20 will be changed to 0, effectively "not caught":
LBsPerDay <- FishCaught * ((BluePercent * replace(BlueLBs, BlueLBs <= 20, 0)) + (YellowPercent * replace(YellowLBs, YellowLBs <= 20, 0)))
str(LBsPerDay)
# num [1:365] 174 291 325 296 232 ...
(No warning, no error.)
If you intend LBsPD to contain all of the weights for each iteration in your simulation, then start with LBsPD <- list(), in which case you'll eventually use
LBsPD <- list()
for (i in 1:5000) {
# ...
LBsPD[[i]] <- LBsPerDay
}
where after 3 (of 5000) iterations, your LBsPD looks like:
str(LBsPD)
# List of 3
# $ : num [1:365] 174 291 325 296 232 ...
# $ : num [1:365] 160.7 97 161.5 145 99.6 ...
# $ : num [1:365] 30.3 121.4 111.7 210.8 139.7 ...
BTW, you might notice that both BlueLBs and YellowLBs have negatives ... not sure if that's a problem, but negative pounds seems suspect. (Because "normal distributions" are by definition unbounded, many things labeled as "normally distributed" are typically not asymptotically compliant. For hasty simulations like this, I often revert to a triangular distribution, which may be normal-enough for some applications, and certainly never gives negative or extremely-positive weights.)
I have two rasters and I want to make the spatial extent of one to another. Then save it as a new raster. I used following code. However, I cannot save the 2013 images with new spatial extent as a new raster. Any guidance is greatly appreciated.
raster_2013 <- raster("avgt2013.tif")
extent(raster_2013)
class : Extent
xmin : 112.91
xmax : 153.64
ymin : -43.75
ymax : -9
> res(raster_2013)
[1] 0.01 0.01
>
> raster_2015 <- raster("avgt2015.tif")
> extent(raster_2015)
class : Extent
xmin : 112
xmax : 154
ymin : -44
ymax : -9
> res(raster_2015)
[1] 0.01 0.01
>
> e <- extent(112, 154, -44, -9)
>
> ex = extent(raster_2015)
> r2 = crop(raster_2013, ex)
>
>
> new_2013 <- alignExtent(e, raster_2013, snap='near')
> str(new_2013)
Formal class 'Extent' [package "raster"] with 4 slots
..# xmin: num 112
..# xmax: num 154
..# ymin: num -44
..# ymax: num -9
>
> rc <- crop(raster_2013, e, snap='near')
> extent(rc)
class : Extent
xmin : 112.91
xmax : 153.64
ymin : -43.75
ymax : -9
First, please make a simple reproducible example to ask a question.
library(raster)
set.seed(11)
raster_2013 = raster(ext=extent(112.91, 153.64, -43.75, -9), res=c(0.01, 0.01))
raster_2013[] = rnorm(ncell(raster_2013))
raster_2015 = raster(ext=extent(112, 154, -44, -9), res=c(0.01, 0.01))
raster_2015[] = rnorm(ncell(raster_2015))
Then, there are several issues with your code.
In your case, alignExtent is useless since the two rasters have the same resolution and their extents correspond with regards to this resolution.
If your goal is to give the extent of raster_2015 to raster_2013, you need to realize that extent(raster_2015) is shorter (smaller) with respect to xmin, but larger or equal elsewhere. So cropping alone will just affect xmin of raster_2013. You first need to extend and second to crop in order to have the exact same extent:
new_2013 <- crop(extend(raster_2013, raster_2015), raster_2015)
all.equal(extent(raster_2015), extent(new_2013))
#[1] TRUE
As #Geo-sp mentions, you can also resample raster_2013, but you would typically use this if the rwo rasters are not aligned (and be aware that it would, in such case, result in modified data due to the interpolation). Here, since they are, it would give the same result as crop(extend()), but it would be much slower and more resource-consuming:
system.time(new_2013 <- crop(extend(raster_2013, raster_2015), raster_2015))
# user system elapsed
# 0.676 0.036 0.712
system.time(new_2013_res <- resample(raster_2013, raster_2015))
# user system elapsed
# 10.324 0.536 10.869
all.equal(new_2013, new_2013_res)
# [1] TRUE
Finally, in order to save it, well... you can use writeRaster, as reading the documentation would have lead you to ;-)
writeRaster(new_2013, "raster_2013_extent2015.grd")