Aggregate to custom (decimal) resolution - r

I have a raster of 30m cell size, with either value 0 or 1. I try to aggregate this into a 1000m cell size. I can then see the sum of '1's, in a 1000m cell size raster.
But the Aggregate function only lets me use a round number as a factor, so the closest I get is with factor 33x (to cell size 990m).
I would then need to resample 990m to 1000m but then obviously the values will shift and be incorrect.
Is there a way to use a decimal factor such as 33,3333, or better yet, use a raster with resolution 1000m as a template for the aggregation?
FOR ILLUSTRATION:
Here's an example of one aggregated cell sourced from 30x30m cells with 0/1 values, now in an aggregate raster at 990x990m cell size:
The two original cells are correctly aggregated into a 990x990m cell, and the sum of all values that were of interest (value '1') is 2. You can see these same two cells in the resampled cell on the right, in the top of the cell. But due to the resampling, the raster has shifted and includes more of the 30x30m cells. Yet, the cell value for the now resampled aggregate raster is still 2, while this is an incorrect value and should be 5.

You can use resample.
Example data:
library(terra)
r <- rast(crs="+proj=utm +zone=1", resolution=30, xmin=0, ymin=0, xmax=1020, ymax=1020)
values(r) <- 1:ncell(r)
I first aggregate to a value close to what you want (in this example, I go from 30 to 100 m spatial resolution), and then use resample
a <- aggregate(r, 3, mean)
rr <- rast(crs="+proj=utm +zone=1", resolution=100, xmin=0, ymin=0, xmax=1000, ymax=1000)
b <- resample(r, rr)
r
#class : SpatRaster
#dimensions : 34, 34, 1 (nrow, ncol, nlyr)
#resolution : 30, 30 (x, y)
#extent : 0, 1020, 0, 1020 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=utm +zone=1 +datum=WGS84 +units=m +no_defs
#source : memory
#name : lyr.1
#min value : 1
#max value : 1156
b
#class : SpatRaster
#dimensions : 10, 10, 1 (nrow, ncol, nlyr)
#resolution : 100, 100 (x, y)
#extent : 0, 1000, 0, 1000 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=utm +zone=1 +datum=WGS84 +units=m +no_defs
#source : memory
#name : lyr.1
#min value : 68.89777
#max value : 1103.335
You could also first disaggregate and then aggregate. With the above example you could do
d <- disaggregate(r, 3)
da <- aggregate(d, 10)
da
#class : SpatRaster
#dimensions : 11, 11, 1 (nrow, ncol, nlyr)
#resolution : 100, 100 (x, y)
#extent : 0, 1100, -80, 1020 (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=utm +zone=1 +datum=WGS84 +units=m +no_defs
#source : memory
#name : lyr.1
#min value : 43
#max value : 1093

The solution was pretty simple, and I highly suspect the phrasing of my question was just not very clear, apologies for that.
When using the aggregate function (whether it is by mean, sum,...) you cannot resample afterwards and expect the agreggated values to still be correct. The values will be preserved, but because cells shift a little or a lot due to resampling, they will not be correct.
The trick as such, is incredibly simple: resample before you aggregate, not afterwards.

Related

Combining 2 raster data sets with different extent, resolution and point regularity

I'm having trouble merging two raster datasets that have different resolution and extent, and have irregular point data. Below is info on each raster. I need these datasets to merge according to the grid points so that I can run a fire spread model (which requires both wind and slope data).
I have tried converting to normal dataframes (using rasterToPoints) before merging but the differences lead to loss of a lot of grid points.
I have tried to align the rasters with project raster and rasterize, but I haven't managed to get it to work. If anyone has an idea that could help, I would really appreciate your answer!
'''
Data from https://globalwindatlas.info/downloads/gis-files
> wind <- raster("ZAF_wind-speed_10m.tif"); wind
class : RasterLayer
dimensions : 11271, 11804, 133042884 (nrow, ncol, ncell)
resolution : 0.0025, 0.0025 (x, y)
extent : 13.33407, 42.84407, -50.31423, -22.13673 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +datum=WGS84 +no_defs
source : C:/Users/s2000128/Documents/Flammability modelling/ZAF/ZAF_wind-speed_10m.tif
names : ZAF_wind.speed_10m
Data from https://datacatalog.worldbank.org/dataset/world-slope-model
> slope <- raster("ZAF1_msk_alt.grd"); slope
class : RasterLayer
dimensions : 1548, 1992, 3083616 (nrow, ncol, ncell)
resolution : 0.008333333, 0.008333333 (x, y)
extent : 16.4, 33, -34.9, -22 (xmin, xmax, ymin, ymax)
crs : +proj=longlat +ellps=WGS84 +no_defs
source : C:/Users/s2000128/Documents/Flammability modelling/slope_deg_0/slope_deg/ZAF1_msk_alt.grd
names : ZAF1_msk_alt
values : -26, 3264 (min, max)
Here are some examples of what I have tried:
1
windresampled <- projectRaster(wind,slope,method = 'ngb'); windresampled
2
wind_points <- rasterToPoints(wind);
coordinates(wind_points) = ~x+y;
proj4string(wind_points) = CRS("+init=epsg:4326");
gridded(wind_points) = TRUE;
g_wind <- raster(wind_points); g_wind;
extent(wind) <- c(16.4,33, -34.9,-22);
res(wind) <- c(0.0025, 0.0025);
r_wind <- rasterize(wind_points, g_wind, field = wind_points$z, fun = mean, na.rm = TRUE); r_wind

Convert 32-bit float raster to 16-bit in R

I have ~100 different 4-band satellite imagery .tif rasters that have floating-point 32 bit depth. I need to convert these to 16-bit unsigned in R while scaling pixel values (not just trashing high values), but I have no idea where to begin for even a single raster, let alone the whole batch. Any help would be much appreciated!
Edit with more info: I searched the raster package documentation for bit, pixel, and depth keywords without much luck. Judging by min/max pixel values information found under dataType(), I want to go from FLT4S (32-bit floating point) to INT2U (16 bit). I tried setting datatype with writeRaster() as shown in the example there, but the output was just a black and white image rather than normal satellite imagery.
image
class : RasterStack
dimensions : 4300, 8909, 38308700, 4 (nrow, ncol, ncell, nlayers)
resolution : 3, 3 (x, y)
extent : 691032, 717759, 57492, 70392 (xmin, xmax, ymin, ymax)
crs : +proj=utm +zone=17 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
names : image.1, image.2, image.3, image.4
min values : 7.244503e-02, 8.278998e-02, 2.286164e-05, 8.571137e-02
max values : 0.5347134, 0.3522218, 0.4896736, 0.7308348
dataType(image) #[1] "FLT4S" "FLT4S" "FLT4S" "FLT4S"
image2 = writeRaster(image, 'new.tif', datatype='INT2U', overwrite=TRUE, format="GTiff")
dataType(image2) #[1] "INT2U"
image2
class : RasterBrick
dimensions : 4300, 8909, 38308700, 4 (nrow, ncol, ncell, nlayers)
resolution : 3, 3 (x, y)
extent : 691032, 717759, 57492, 70392 (xmin, xmax, ymin, ymax)
crs : +proj=utm +zone=17 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
source : new.tif
names : new.1, new.2, new.3, new.4
min values : 0, 0, 0, 0
max values : 1, 0, 0, 1
In your example you have real values between 0 and 1. By writing to an integer type, these are all truncated to 0. If you want to write to INT2U (unsigned byte) you could first scale the values to be between 0 and 255.
Example data
library(raster)
b <- brick(system.file("external/rlogo.grd", package="raster"))
image <- clamp(b/255, 0, 0.6)
#class : RasterBrick
#dimensions : 77, 101, 7777, 3 (nrow, ncol, ncell, nlayers)
#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 : red, green, blue
#min values : 0, 0, 0
#max values : 0.6, 0.6, 0.6
What you do (truncation)
fname <- paste0(tempfile(), ".tif")
x <- writeRaster(image, fname, datatype='INT2U', overwrite=TRUE)
x
#min values : 0, 0, 0
#max values : 1, 1, 1
But note the difference when you use rounding
fname <- paste0(tempfile(), ".tif")
y <- round(image)
z <- writeRaster(y, fname, datatype='INT2U', overwrite=TRUE)
s <- stack(x[[1]], z[[1]])
plot(s)
Now with some scaling
maxv <- 65535
r <- round(image * maxv)
fname <- paste0(tempfile(), ".tif")
s <- writeRaster(r, fname, datatype='INT2U', overwrite=TRUE)
#s
#min values : 0, 0, 0
#max values : 39321, 39321, 39321
With your data you would get max values of
round(maxv * c(0.5347134, 0.3522218, 0.4896736, 0.7308348 ))
#[1] 35042 23083 32091 47895
You can also choose to set the max values of all layers to maxv, to keep more variation (but make the values no longer comparable with other data) --- more relevant if you were using a smaller range such as 0-255.
ss <- round(maxv * image / maxValue(image))
#names : red, green, blue
#min values : 0, 0, 0
#max values : 65535, 65535, 65535
the above works because the lowest values are zero; if you have negative values you would do
ss <- image - minValue(image)
ss <- round(maxv * ss / maxValue(ss))
In other cases you might want to use clamp. So you need to decide how to scale
What I showed is linear scaling. There are other ways. For example, there is the is also the scale method, to improve the statistical distribution of numbers. That may be relevant; but it depends on what your goals are.
Note. You do not say why you do this, and that is OK. But if it is to save hard disk space, you can use compression instead (see ?writeRaster)

How does R assign a resolution to raster objects?

Suppose one runs the following R code
install.packages("raster")
library(raster)
r <- raster(ncol=18, nrow=18)
res(r)
The output of the res function is
[1] 20 10
How are these values defined? How does the raster function calculate them? In what units are they expressed?
As pointed out by Guillaume Devailly, the horizontal resolution is the horizontal extent divided by the number of columns. The vertical resolution is the vertical extent divided by the number of rows. The units are the units of your coordinate reference system. The default is degrees (for longitude/latitude). To add more to Guillaume's answer:
Create a raster with 10 rows and columns that goes from 0 to 10. The resolution is 1.
library(raster)
r <- raster(ncol=10, nrow=10, xmn=0, xmx=10, ymn=0, ymx=10)
r
#class : RasterLayer
#dimensions : 10, 10, 100 (nrow, ncol, ncell)
#resolution : 1, 1 (x, y)
#extent : 0, 10, 0, 10 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
Change the resolution to 0.5; the number of rows and columns double
res(r) <- 0.5
r
#class : RasterLayer
#dimensions : 20, 20, 400 (nrow, ncol, ncell)
#resolution : 0.5, 0.5 (x, y)
#extent : 0, 10, 0, 10 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
You can change the resolution indirectly by adjusting the extent
extent(r) <- c(0,5,0,5)
r
#class : RasterLayer
#dimensions : 20, 20, 400 (nrow, ncol, ncell)
#resolution : 0.25, 0.25 (x, y)
#extent : 0, 5, 0, 5 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
The x and y resolution can be set to a different value
res(r) <- c(1, 0.5)
When you change the resolution directly, via res any cell values associated with the Raster* object are lost; because the number of rows or columns has to change. If you change it indirectly, by changing the extent, the values stay.
From what I understand from the vignette
The default settings will create a global raster data structure with a longitude/latitude coordinate reference system and 1 by 1 degree cells.
r
# class : RasterLayer
# dimensions : 18, 18, 324 (nrow, ncol, ncell)
# resolution : 20, 10 (x, y)
# extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
# coord. ref. : +proj=longlat +datum=WGS84
r x extent goes to from -180 to +180 degrees by default (a total of 360 degrees), and 360 degrees / 18 points = a x resolution of 20 degrees.
r y extent goes form -90 to +90 degrees by default, and 180 degrees / 18 points results in a y resolution of 10 degrees.

How do I recode non-standard date dimension of a netcdf file in R?

I'm looking for a tip on how to recode time/date dimension of a netcdf file/raster brick. I extracted a monthly temperature data and the date dimension is not in the standard date format. For example, 1850.04166666667, 1850.125, means January and February 1850 respectively in the data and R couldn't understand them. Here's the data structure
> eco.sst
class : RasterBrick
dimensions : 180, 360, 64800, 2030 (nrow, ncol, ncell, nlayers)
resolution : 1, 1 (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 : C:\Users\Admin\AppData\Local\Temp\Rtmp6vFWNG\raster\r_tmp_2019-04-18_183528_11516_79080.grd
names : X1850.04166666667, X1850.125, X1850.20833333333, X1850.29166666667, X1850.375, X1850.45833333333, X1850.54166666667, X1850.625, X1850.70833333333, X1850.79166666667, X1850.875, X1850.95833333333, X1851.04166666667, X1851.125, X1851.20833333333, ...
min values : -0.668686926, -0.273689777, -1.902773499, -3.406341553, -3.626811743, -2.131400585, -1.569969416, -1.630665064, -2.418994188, -1.971702099, -0.822018623, -3.423746109, -1.303600550, -0.786648691, -1.452626705, ...
max values : 3.926926e+00, 1.550823e+00, 1.151636e+00, 6.622851e-01, 9.673859e-01, 7.178870e-01, 9.010030e-01, 4.363060e-01, 5.231520e-01, 3.346115e-01, 2.156055e-01, 6.418970e-01, 2.259051e+00, 3.802529e+00, 2.077996e+00, ...
time : 1850.04166666667, 2019.125 (min, max)
I wrote a function to replace the decimal points;
fun.repl = function (x, na.rm = TRUE) str_replace(x, ".04166666667", "-1")
such that 1850.04166666667, which means January 1850 becomes 1850-1;
which I then applied to the raster in the form:
stackApply(eco.sst, "time", fun.repl)
eco.sst is the raster brick while "time" is referencing the time dimension. But the function did not work. I suspect that I don't understand the data structure well enough to successfully apply the function. I'm new to this and also finding out that this is a rare problem with netcdf.
I expect 1850.04166666667, 1850.125, 1850.20833333333, 1850.29166666667...which means January to April 1850 to become standard dates in the form.... 1850-1, 1850-2, 1850-3, 1850-4 etc
Here is another way to get the dates
todate <- function(x) {
year <- trunc(x)
month <- round(12 * ((x - year) + 1/24))
as.Date(paste0(year, "-", month, "-15"))
}
dts <- c(1850.04166666667, 1850.125)
d <- todate(dts)
d
#[1] "1850-01-15" "1850-02-15"
Note that I use the 15th day for each month in order to get a valid date --- but that seems to be what is intended.
Now you probably want to use setZ, see the example below. (stackApply makes no sense here and the arguments you use are not valid either)
library(raster)
r <- raster(ncol=10, nrow=10)
s <- stack(lapply(1:2, function(x) setValues(r, runif(ncell(r)))))
s <- setZ(s, d)
s
#class : RasterStack
#dimensions : 10, 10, 100, 2 (nrow, ncol, ncell, nlayers)
#resolution : 36, 18 (x, y)
#extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
#crs : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
#names : layer.1, layer.2
#min values : 0.01477963, 0.01178438
#max values : 0.9980334, 0.9939610
#time : 1850-01-15, 1850-02-15

Find the coordinates of a specific pixel in rasterStack

I would like to find the pixels coordinates in a rasterStack that have the same pattern of a vector. Below is a simple example.
> s<-r<-raster(ncol=5,nrow=5)
> r[]<-round(runif(ncell(r)))
> s[]<-2
> rs<-stack(r,s)
> rs
class : RasterStack
dimensions : 5, 5, 25, 2 (nrow, ncol, ncell, nlayers)
resolution : 72, 36 (x, y)
extent : -180, 180, -90, 90 (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0
names : layer.1, layer.2
min values : 0, 2
max values : 1, 2
If I have a vector vet<-c(0,2), which the pixels coordinates that have value 0 in the first layer and value 2 in the second one?
Playing with which might work. I'm sticking with matrices; you can rasterize them later :-)
foo<-which(r==0,arr.ind=TRUE)
s<-matrix(ncol=5,nrow=5)
set.seed(10)
r[]<-round(runif(ncell(r)))
s[1:3,]<-2
foo<-which(r==0,arr.ind=TRUE)
bar<-which(s==2,arr.ind=TRUE)
Then look for identical rows in foo and bar, perhaps by concatenating rows, or calculating foo-bar and identifying zero rows.
EDIT: a better way, giving you the coordinates right away.
which(s==2 & r==0, arr.ind=TRUE)

Resources