Set the right coordinate system for SpatRaster in terra - r

I want to map species occurrences and sampling stations on an IBCSO map using the package terra in R. I have my sampling points as latitude, longitude. I don't know how I should define the coordinate system. If I plot my coordinates they all cuddle together at the center of the map. I guess the extend values have to be adjusted. How can I do that without distorting the map?
class : SpatRaster
dimensions : 19200, 19200, 3 (nrow, ncol, nlyr)
resolution : 500, 500 (x, y)
extent : -4800000, 4800000, -4800000, 4800000 (xmin, xmax, ymin, ymax)
coord. ref. : WGS 84 / IBCSO Polar Stereographic (EPSG:9354)
source : IBCSO.tif
colors RGB : 1, 2, 3
names : IBCSO_1, IBCSO_2, IBCSO_3
min values : 12, 18, 24
max values : 254, 254, 254
enter image description here

You have coordinates in lon/lat and want to match these to the raster data.
Example data
library(terra)
x <- rast(nrow=19200, ncol=19200, ext=c(-4800000, 4800000, -4800000, 4800000), crs="EPSG:9354")
lon <- 1:10
lat <- -(77:86)
Solution
v <- vect(cbind(lon, lat), crs="+proj=longlat")
p <- project(v, crs(x))

Related

disaggregate raster value to finer cells equally

I have a SpatRaster in the following dimension
layer1
class : SpatRaster
dimensions : 3600, 3600, 1 (nrow, ncol, nlyr)
resolution : 0.0002777778, 0.0002777778 (x, y)
extent : -81.00014, -80.00014, 24.99986, 25.99986 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84 (EPSG:4326)
source : nn.tif
name : nn_1
I have another SpatRaster which has data for revenue (in dollar) at a different resolution than layer1
layer2
class : SpatRaster
dimensions : 21600, 43200, 1 (nrow, ncol, nlyr)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84
source : XX.nc
varname : XX
name : XX
unit : US dollar
What I want to do is to take the average of layer1 using layer2 as weights. This is my approach:
Give them the same projections.
layer2 <- project(layer2, crs(layer1))
Clip layer2 to match the extent of layer1
r <- terra::crop(layer2, ext(layer1))
r <- terra::mask(r, layer1) # I get error in this step
[mask] number of rows and/or columns do not match
Suppose step 2 worked. Now I want to disaggregate clipped layer2 to match the resolution of layer1 i.e. 30 times finer.
This means revenue of each disaggregated cell should be 1/30 of the revenue of the parent cell.
Is the below the right way to do this:
r_disagg <- disaggregate(r, fact = 30)/30
Divide each cell value in r_disagg with total sum of r_disagg to get a weight raster r_disagg_wt
Multiply r_disagg_wt with layer1 and add the resulting raster to get the weighted average of layer1
I am stuck in step 2 and 3
When asking an R question, please include example data like this
Example data
library(terra)
r1 <- rast(nrow=360, ncol=360, ext=c(-81.00014, -80.00014, 24.99986, 25.99986))
r2 <- rast(nrow=2160, ncol=4320)
values(r1) <- 1:ncell(r1)
set.seed(0)
values(r2) <- runif(ncell(r2))
Solution
re2 <- resample(r2, r1)
global(r1, fun="mean", weights=re2)
# weighted_mean
#lyr.1 66190.15
In cases where the two input rasters align, it could be preferable to use disagg instead of resample (in this case, the extent of r1 is a bit odd, it seems to be shifted with half a cell).
There is no need to adjust the dollar values if they are only used as weights. In cases that the sum of the cell values should remain constant, when using resample or project, you could compute the density ($/km2) before resampling by dividing the values with the area of each cell (see cellSize) and multiply the values again with the area of the new cells after resampling.

Error calculating area of raster with lat/lon projection

I have a global raster stack (of three rasters) whose pixel values are the percent of a land use for that pixel. Here's the raster metadata:
class : RasterBrick
dimensions : 3600, 7200, 25920000, 3 (nrow, ncol, ncell, nlayers)
resolution : 1, 1 (x, y)
extent : 0, 7200, 0, 3600 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : grass_baseline.tif
names : grass_2020, grass_2040, grass_2100
I'm trying to calculate the total area of land use in each pixel by multiplying the pixel value by the area of the raster, using the area() function in the raster package.
When I do that, I get the following error:
Warning message:
In .couldBeLonLat(x, warnings = warnings) :
raster has a longitude/latitude CRS, but coordinates do not match that
Here's the metadata for the area raster:
class : RasterLayer
dimensions : 3600, 7200, 25920000 (nrow, ncol, ncell)
resolution : 1, 1 (x, y)
extent : 0, 7200, 0, 3600 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : memory
names : layer
values : -710.0924, 2211922 (min, max)
Does anyone have any insight into what might be going on?
In case it's relevant, I assembled this raster stack from a few .nc files that I read into R with the ncdf4 package and converted to rasters with the following line of code:
raster(first_nc, xmn=0, xmx=7200, ymn=0, ymx=3600, crs=CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs+ towgs84=0,0,0")
I then combined several of these rasters together as a stack and exported using the stars package (to preserve the names of each raster):
stack <- stack(first_nc,second_nc,third_nc)
names(stack) <- c('first_nc','second_nc','third_nc')
stars::write_stars(stars::st_as_stars(stack), "stack.tif")
I then read the .tif into a separate script, which is where I'm trying to calculate the area.
You have
#extent : 0, 7200, 0, 3600 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +no_defs
That is, a latitude between 0 and 3600 degrees. That makes no sense as you cannot go beyond 90 degrees N and it is thus not possible to compute area for these cells. And the specified longitude is not likely to be correct either, unless your data really covers the globe 20 times.
I assembled this raster stack from a few .nc files that I read into R with the ncdf4 package and converted to rasters
That is not a good approach (unless all else fails), and explains the odd extent. What you should try first is
library(raster)
s <- stack(filenames)
Or better use the terra package (the replacement of raster)
library(terra)
s <- rast(filenames)
It should not be necessary, but if you are going to set the extent yourself, more plausible values would be (-180, 180, -90, 90), or (0, 360, -90, 90).

How to run different formula on different layers in R

I downloaded worlclim/BIO climatic data which has 16 layers. 1-11 layers of which are temperature data. Rests are precipitation data. When I checked document, I should convert unit of temperature data by different conversion factors. 1-2,4-11 layers should be divided by 10 to convert degree celcius and 3-4 layers by 100. To do this, I wrote following code:
temp1<-clim[[1:2]]/10
temp2 <-clim[[5:11]]/10
temp3<-clim[[3:4]]/100
Stack them back according to the same order as they were in original data:
clim <-stack(temp1,temp3,temp2)
My question is how to calculate different formula on different layer and stack them back to original order? I want to know another way to do these steps.
Thank you!
Easist way could be to define a vector of "dividing factors" and then simply divide the stack by that vector. In this way, you do not need to put the bands in the "original" order:
library(raster)
a <- raster::raster(ncols = 10, nrows = 10)
a <- raster::init(a, runif)
# create a stack
b <- raster::stack(a,a,a,a,a,a)
# define vector of dividing factors
divs <- c(1,1,10,10,100,100)
# compute
c <- b / divs
c
class : RasterBrick
dimensions : 10, 10, 100, 6 (nrow, ncol, ncell, nlayers)
resolution : 36, 18 (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.1, layer.2, layer.3, layer.4, layer.5, layer.6
min values : 5.919103e-03, 5.919103e-03, 5.919103e-04, 5.919103e-04, 5.919103e-05, 5.919103e-05
max values : 0.99532098, 0.99532098, 0.09953210, 0.09953210, 0.00995321, 0.00995321

R how to get a vector of latitudes from a regular tif using brick

I have a tif file that I read in using brick. I can see the spatial extend of the tif file:
b<-brick("t.tif")
b
class : RasterBrick
dimensions : 10, 10, 100, 1 (nrow, ncol, ncell, nlayers)
resolution : 0.0001851853, 0.0001851854 (x, y)
extent : -18.61944, -18.61759, 37.83856, 37.84041 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : /scratch/tompkins/water_allafrica/t.tif
names : t
min values : 0
max values : 255
extent(SpatialPoints(b))
class : Extent
xmin : -18.61935
xmax : -18.61769
ymin : 37.83865
ymax : 37.84031
But I can't work out how to get a vector of latititudes and longitudes easily that I need to define a netcdf file header which I want to write out. I could do it manually but I am presuming there is a built in function that is easier to use.
Example input file here: http://clima-dods.ictp.it/Users/tompkins/stackoverflow/t.tif
This might be what you need:
ext <- extent(b)
lat <- seq(ext#ymin,ext#ymax,res(b)[2])
lon <- seq(ext#xmin,ext#xmax,res(b)[1])
So basically your're creating a sequence vector from x/y min to max with spacing of the brick's resolution.
The values refer to the corner coordinates of the cell ... you could be also interested in the cell centers.
Just for illustration:
# create testraster
x <- raster(resolution=c(40,40))
x[]<- 1:ncell(x)
# plot
plot(x)
# add corner coordinates
plot(SpatialPoints(cbind(rep(extent(x)#xmin,10),seq(extent(x)#ymin,extent(x)#ymax,res(x)[2])),proj4string = crs(x)),
col='red',pch='*',cex=5,add=T)
# add cell centers
plot(SpatialPoints(xyFromCell(x,cellFromRowCol(x,1:nrow(x),1)),proj4string = crs(x)),
col='blue',pch='*',cex=5,add=T)
So the method above gives you the latitudes indicated by the red asterisks. If you need the blue ones, you could use xyFromCell which returns the coordinates of a raster's cell.

Can't change raster's extent

I want to crop an elevation raster to add it to a raster stack. It's easy, I did this before smoothly, adding a ecoregions raster to the same stack. But with the elevation one, just doesn't work. Now, there are several questions here in overflow adressing this issue and I tryed a lot of things...
First of all, we need this:
library(rgdal)
library(raster)
My stack is predictors2:
#Downloading the stack
predictors2_full<-getData('worldclim', var='bio', res=10)
#Cropping it, I don' need the whole world
xmin=-120; xmax=-35; ymin=-60; ymax=35
limits <- c(xmin, xmax, ymin, ymax)
predictors2 <- crop(predictors2_full,limits)
Then I've downloaded the terr_ecorregions shapefile here: http://maps.tnc.org/files/shp/terr-ecoregions-TNC.zip
setwd("~/ORCHIDACEAE/Ecologicos/w2/layers/terr-ecoregions-TNC")
ecoreg = readOGR("tnc_terr_ecoregions.shp") # I've loaded...
ecoreg2 <- crop(ecoreg,extent(predictors2)) # cropped...
ecoreg2 <- rasterize(ecoreg2, predictors2) # made the shapefile a raster
predictors4<-addLayer(predictors2,elevation,ecoreg2) # and added the raster
# to my stack
With elevation, I just can't. The Digital elevation model is based in GMTED2010, which can be downloaded here: http://edcintl.cr.usgs.gov/downloads/sciweb1/shared/topo/downloads/GMTED/Grid_ZipFiles/mn30_grd.zip
elevation<-raster("w001001.adf") #I've loaded
elevation<-crop(elevation,predictors2) # and cropped
But elevation gets a slightly different extent instead of predictors2's extent:
> extent(elevation)
class : Extent
xmin : -120.0001
xmax : -35.00014
ymin : -60.00014
ymax : 34.99986
>
I tried to make then equal by all means I read about in questions here...
I tried to extend so elevation's ymax would meet predictors2's ymax
elevation<-extend(elevation,predictors2) #didn't work, extent remains the same
I tried the opposite... making predictors2 extent meet elevation's extent... nothing either.
But then I read that
You might not want to play with setExtent() or extent() <- extent(), as you could end with wrong geographic coordinates of your rasters - #ztl, Jun 29 '15
And I tried to get the minimal common extent of my rasters, following #zlt answer in another extent question, by doing this
# Summing your rasters will only work where they are not NA
r123 = r1+r2+r3 # r123 has the minimal common extent
r1 = crop(r1, r123) # crop to that minimal extent
r2 = crop(r2, r123)
r3 = crop(r3, r123)
For that, first I had to set the resolutions:
res(elevation)<-res(predictors2) #fixing the resolutions... This one worked.
But then, r123 = r1+r2+r didn't work:
> r123=elevation+ecoreg2+predictors2
Error in elevation + ecoreg2 : first Raster object has no values
Can anyone give me a hint on this? I really would like to add my elevation to the raster. Funny thing is, I have another stack named predictors1 with the exact same elevation's extent... And I was able to crop ecoreg and add ecoreg to both predictors1 and predictors2... Why can't I just do the same to elevation?
I'm quite new to this world and runned out of ideas... I appreciate any tips.
EDIT: Solution, Thanks to #Val
I got to this:
#Getting the factor to aggregate (rasters are multiples of each other)
res(ecoreg2)/res(elevation)
[1] 20 20 #The factor is 20
elevation2<-aggregate(elevation, fact=20)
elevation2 <- crop(elevation2,extent(predictors2))
#Finally adding the layer:
predictors2_eco<-addLayer(predictors2,elevation2,ecoreg)
New problem, thought...
I can't write stack to a geotiff
writeRaster(predictors2_eco, filename="cropped_predictors2_eco.tif", options="INTERLEAVE=BAND", overwrite=TRUE)
Error in .checkLevels(levs[[j]], value[[j]]) :
new raster attributes (factor values) should be in a data.frame (inside a list)
I think you're having issues because you're working with rasters of different spatial resolutions. So when you crop both rasters to the same extent, they'll have a slightly different actual extent because of that.
So if you want to stack rasters, you need to get them into the same resolution. Either you disaggregate the raster with the coarser resolution (i.e. increase the resolution by resampling or other methods) or you aggregate the raster with the higher resolution (i.e. decrease the resolution with for instance taking the mean over n pixel).
Please note that if you change the extent or resolution with setExtent(x), extent(x) <-, res(x) <- or similar will NOT work, since you're just changing slots in the raster object, not the actual underlying data.
So to bring the rasters into a common resolution, you need to change the data. You can use the functions (amongst others) aggregate, disaggregate and resample for that purpose. But since you're changing data, you need to be clear on what you're and the function you use is doing.
The most handy way for you should be resample, where you can resample a raster to another raster so they match in extent and resolution. This will be done using a defined method. Per default it's using nearest neighbor for computing the new values. If you're working with continuous data such as elevation, you might want to opt for bilinear which is bilinear interpolation. In this case you're actually creating "new measurements", something to be aware of.
If your two resolutions are multiples of each other, you could look into aggregate and disaggregate. In the case of disaggregate you would split a rastercell by a factor to get a higher resolution (e.g. if your first resolution is 10 degrees and your desired resolution is 0.05 degrees, you could disaggregate with a factor of 200 giving you 200 cells of 0.05 degree for every 10 degree cell). This method would avoid interpolation.
Here's a little working example:
library(raster)
library(rgeos)
shp <- getData(country='AUT',level=0)
# get centroid for downloading eco and dem data
centroid <- coordinates(gCentroid(shp))
# download 10 degree tmin
ecovar <- getData('worldclim', var='tmin', res=10, lon=centroid[,1], lat=centroid[,2])
ecovar_crop <- crop(ecovar,shp)
# output
> ecovar_crop
class : RasterBrick
dimensions : 16, 46, 736, 12 (nrow, ncol, ncell, nlayers)
resolution : 0.1666667, 0.1666667 (x, y)
extent : 9.5, 17.16667, 46.33333, 49 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : tmin1, tmin2, tmin3, tmin4, tmin5, tmin6, tmin7, tmin8, tmin9, tmin10, tmin11, tmin12
min values : -126, -125, -102, -77, -33, -2, 19, 20, 5, -30, -74, -107
max values : -31, -21, 9, 51, 94, 131, 144, 137, 106, 60, 18, -17
# download SRTM elevation - 90m resolution at eqt
elev <- getData('SRTM',lon=centroid[,1], lat=centroid[,2])
elev_crop <- crop(elev, shp)
# output
> elev_crop
class : RasterLayer
dimensions : 3171, 6001, 19029171 (nrow, ncol, ncell)
resolution : 0.0008333333, 0.0008333333 (x, y)
extent : 9.999584, 15.00042, 46.37458, 49.01708 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
data source : in memory
names : srtm_39_03
values : 198, 3865 (min, max)
# won't work because of different resolutions (stack is equal to addLayer)
ecoelev <- stack(ecovar_crop,elev_crop)
# resample
elev_crop_RS <- resample(elev_crop,ecovar_crop,method = 'bilinear')
# works now
ecoelev <- stack(ecovar_crop,elev_crop_RS)
# output
> ecoelev
class : RasterStack
dimensions : 16, 46, 736, 13 (nrow, ncol, ncell, nlayers)
resolution : 0.1666667, 0.1666667 (x, y)
extent : 9.5, 17.16667, 46.33333, 49 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
names : tmin1, tmin2, tmin3, tmin4, tmin5, tmin6, tmin7, tmin8, tmin9, tmin10, tmin11, tmin12, srtm_39_03
min values : -126.0000, -125.0000, -102.0000, -77.0000, -33.0000, -2.0000, 19.0000, 20.0000, 5.0000, -30.0000, -74.0000, -107.0000, 311.7438
max values : -31.000, -21.000, 9.000, 51.000, 94.000, 131.000, 144.000, 137.000, 106.000, 60.000, 18.000, -17.000, 3006.011

Resources