reading tif file using terra and raster package gives different results - r

I am reading a raster file using both terra and raster package
library(raster)
library(terra)
fl_terra <- terra::rast('my_raster.tif')
fl_raster <- raster::raster('my_raster.tif')
fl_terra
class : SpatRaster
dimensions : 157450, 327979, 1 (nrow, ncol, nlyr)
resolution : 0.0002777778, 0.0002777778 (x, y)
extent : -142.4107, -51.30541, 41.12569, 84.86181 (xmin, xmax, ymin, ymax)
coord. ref. : lon/lat WGS 84 (EPSG:4326)
source : xxxx.tif
categories : DepthBand
name : DepthBand
min value : 0.0m <= x <= 0.3m
max value : x > 9.0m
fl_raster
class : RasterLayer
dimensions : 157450, 327979, 51640293550 (nrow, ncol, ncell)
resolution : 0.0002777778, 0.0002777778 (x, y)
extent : -142.4107, -51.30541, 41.12569, 84.86181 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : xxxx.tif
names : DepthBand
values : 1, 6 (min, max)
Why is reading the same file using the two packages showing different values?
If I open the same file in QGIS, the legend displays the values present in the fl_raster i.e. using the raster package.

The file has categorical values. fl_terra tells you that:
categories : DepthBand
And it also properly shows the (alphabetical) range of the categories (and it also uses them in the legend when using plot, and returns them when extracting cell values).
name : DepthBand
min value : 0.0m <= x <= 0.3m
max value : x > 9.0m
If you do not care about the categories, you can remove them with
levels(fl_terra) <- NULL
In contrast, fl_raster shows the numerical codes that are used to represent the categories (because the raster files can only store numeric cell values).
values : 1, 6 (min, max)
That makes it look like you have numeric values. But that is not the case.
The situation is similar to having a factor in R
f <- as.factor(c("red", "blue", "green"))
"terra" would show the category labels
f
#[1] red blue green
#Levels: blue green red
Whereas "raster" would show the equivalent of
as.integer(f)
#[1] 3 1 2

Related

Read Sentinel-2 bands into R retrieves high values

When I directly read into R the jp2 band files I get unusual high values compared to when I read the files in SNAP (version 9). To read the bands into R I use terra package (you can also use raster package) and the values range from 0 to 18000 more or less. I was wondering if SNAP is doing some conversion that I am not aware of to show values that range from 0 to 0.15 more or less.
> r_10
class : SpatRaster
dimensions : 10980, 10980, 4 (nrow, ncol, nlyr)
resolution : 10, 10 (x, y)
extent : 499980, 609780, 6690240, 6800040 (xmin, xmax, ymin, ymax)
coord. ref. : WGS 84 / UTM zone 32N (EPSG:32632)
sources : T32VNN_20181018T105031_B02_10m.jp2
T32VNN_20181018T105031_B03_10m.jp2
T32VNN_20181018T105031_B04_10m.jp2
... and 1 more source(s)
names : B02_10m_m10_2018, B03_10m_m10_2018, B04_10m_m10_2018, B08_10m_m10_2018
min values : 0, 0, 0, 0
max values : 18815, 17880, 17023, 15608
>
I have tried to export the bands from SNAP into TIF to see if it is a problem of format but it takes forever. I was hoping that there is a convesion factor to show the actual values that I need for my analysis.
SNAP applies a scaling factor of 1.0E4 so that values can be stored more efficiently as integers. You will need to either divide the values by this scaling factor in R, or else use the scaled units to benefit from more efficient integer arithmetic. See https://step.esa.int/docs/tutorials/Exporting%20data%20from%20SNAP.pdf for more details
Legacy formats ([Geo]TIFF, JPEG) have incomplete metadata support. You may lose missing value codes, offset and scale factors, processing history, etc. NetCDF4-CF has good metadata support for applications that actually use what is available. The R terra package can import NetCDF4-CF format, but is selective about what metadata are imported. For the files I have tested, missing data, scale, and offset values are used by the rast() function, but other metadata are lost.
With terra you can also set a scale/offset if you want:
library(terra)
#terra 1.6.21
f <- system.file("ex/elev.tif", package="terra")
r <- rast(f)
r
#class : SpatRaster
#dimensions : 90, 95, 1 (nrow, ncol, nlyr)
#resolution : 0.008333333, 0.008333333 (x, y)
#extent : 5.741667, 6.533333, 49.44167, 50.19167 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84 (EPSG:4326)
#source : elev.tif
#name : elevation
#min value : 141
#max value : 547
scoff(r) <- cbind(10, 0)
r
#class : SpatRaster
#dimensions : 90, 95, 1 (nrow, ncol, nlyr)
#resolution : 0.008333333, 0.008333333 (x, y)
#extent : 5.741667, 6.533333, 49.44167, 50.19167 (xmin, xmax, ymin, ymax)
#coord. ref. : lon/lat WGS 84 (EPSG:4326)
#source : elev.tif
#name : elevation
#min value : 1410
#max value : 5470
Essentially this is way to delay the evaluation of value * scale + offset, but now it may need to be done multiple times (each time that r is used), so it is not something I would generally recommend doing.

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.

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.

Calculating area in a binary raster

I ran some species distribution models (SDM), using maxent of the 'dismo' package for several species, so I saved each one as a raster file.
After that I transformed this environmental suitability raster (SDM result) into a binary map based on a determined threshold value.
I used this function and these command lines to do the conversion:
bin <- function(x) {
ifelse(x <= 0.7, 0,
ifelse(x > 0.7, 1, NA)) }
sp_bin <- calc(spp, fun=bin) # "spp" is the object within my raster
writeRaster(sp_bin,filename='Results/RasterBin/Patagioenas_fasciata_bin.tif')
Now I have a raster of that binary map (i.e., with only two values, 0 and 1).
class : RasterLayer
dimensions : 1117, 1576, 1760392 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -68.39167, -55.25833, -0.7333333, 8.575 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
data source : C:\Users\Ramiro\Desktop\SDM\Results\RasterBin\Patagioenas_fasciata_bin.tif
names : Patagioenas_fasciata_bin
values : 0, 1 (min, max)
I tried to use the following argument to bring the sum of the pixels with value 1, assuming that the others would be 0 and therefore the total sum would equal the area of all pixels with values 1:
cellStats(spp, stat='sum', na.rm=TRUE)
And I got a numerical result (12,471) that I thought was correct. However, I did this for other species, and therefore other rasters, and the result remains the same, although each species has a distinct binary map.
How can I calculate the area only for pixels with value "1" in a binary map raster in R?
Thank you all for the attention.

Calculating area of occupancy from a binary unprojected raster

I have a series of binary raster layers (ascii file) showing presence/absence of a species in Europe and Africa. The file is based on unprojected lat/long (WGS84) data. My aim is to calculate the area of presence using R (I don't have access to ArcGIS).
I know that the raster package has a function for calculating area, but I'm worried that this won't be accurate for unprojected data. I have also looked at the cellStats function in the raster package, and can use this to "sum" the number of cells occupied, but I feel this has the same problem.
jan<-raster("/filelocation/file.asc")
jan
class : RasterLayer
dimensions : 13800, 9600, 132480000 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -20, 60, -40, 75 (xmin, xmax, ymin, ymax)
coord. ref. : NA
data source : "/filelocation"
names : file.asc
values : -2147483648, 2147483647 (min, max)
area(jan)
class : RasterLayer
dimensions : 13800, 9600, 132480000 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -20, 60, -40, 75 (xmin, xmax, ymin, ymax)
coord. ref. : NA
names : layer
values : 6.944444e-05, 6.944444e-05 (min, max)
Warning messages:
1: In .local(x, ...) :
This function is only useful for Raster* objects with a longitude/latitude coordinates
2: In .rasterFromRasterFile(grdfile, band = band, objecttype, ...) :
size of values file does not match the number of cells (given the data type)
cellStats(jan,"sum")
[1] 3559779
Anybody know of a way to calculate the presence area accurately, accounting for the earth curvature?
Thanks!
I do not know what is going in with your file (why you get warning #2). But is here is a work around
r <- raster(nrow=13800, ncol=9600, xmn=-20, xmx=60, ymn=-40, ymx=75)
# equivalent to r <- raster(jan)
x = area(r)
x
class : RasterLayer
dimensions : 13800, 9600, 132480000 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : -20, 60, -40, 75 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84
data source : c:\temp\R_raster_Robert\2015-01-26_213612_1208_85354.grd
names : layer
values : 0.2227891, 0.8605576 (min, max)
Now you have the area of each cell in km2. By multiplying these values with Raster objects with presence/absence values and then using cellStats( , 'sum') you can obtain the total area with presence.

Resources