Land cover: aggregate to 1 km resolution
Aggregate land cover at 1km to obtain counts per class in a range from 0 to 1600.
Load packages, read data and source custom scripts
rm(list = ls())
library(stars)
#> Loading required package: abind
#> Loading required package: sf
#> Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
library(purrr)
path_proj <- day2day::git_path()
path_data <- file.path(path_proj, "data")
path_raw <- file.path(path_data, "raw")
path_cleaned <- file.path(path_data, "cleaned")
path_processed <- file.path(path_data, "processed")
uk <- st_read(file.path(path_cleaned, "uk_simple.gpkg"), "union_buffer")
#> Reading layer `union_buffer' from data source `/home/rstudio/Documents/Projects/land-suitability/data/cleaned/uk_simple.gpkg' using driver `GPKG'
#> Simple feature collection with 1 feature and 0 fields
#> geometry type: MULTIPOLYGON
#> dimension: XY
#> bbox: xmin: -5115.571 ymin: 2055.298 xmax: 660644.8 ymax: 1223625
#> projected CRS: OSGB 1936 / British National Grid
uk_bbox_1km <- read_stars(file.path(path_processed, "uk_bbox_1km.tif"))
classes <- 0:7
lcover_files <- file.path(path_cleaned, paste0("landcover_reclass_", classes ,".tif"))
lcover <- map(lcover_files, ~ read_stars(., proxy = TRUE))
Custom functions for computing counts
# counts per axis from original grid to new grid
st_count <- function(src, dest) {
dd <- dim(dest)
x_freq <- setNames(rep(0, dd[1]), seq_len(dd[1]))
y_freq <- setNames(rep(0, dd[2]), seq_len(dd[2]))
x_seq_g <- st_get_dimension_values(dest, "x", center = FALSE)
y_seq_g <- st_get_dimension_values(dest, "y", center = FALSE)
x_seq <- st_get_dimension_values(src, "x", center = NA)
y_seq <- st_get_dimension_values(src, "y", center = NA)
x_delta <- st_dimensions(dest)[["x"]]$delta
y_delta <- st_dimensions(dest)[["y"]]$delta
x_freq_aux <- table(floor((x_seq - min(x_seq_g)) / x_delta) + 1)
y_freq_aux <- table(floor((y_seq - max(y_seq_g)) / y_delta) + 1)
x_freq_aux <- x_freq_aux[names(x_freq_aux) %in% names(x_freq)]
y_freq_aux <- y_freq_aux[names(y_freq_aux) %in% names(y_freq)]
x_freq[names(x_freq_aux)] <- x_freq_aux
y_freq[names(y_freq_aux)] <- y_freq_aux
return(list(x_freq = x_freq, y_freq = y_freq))
}
# converting average to sum
st_warp_to_sum <- function(src, dest) {
counts <- st_count(src, dest)
dest[[1]] <- dest[[1]] * counts[[1]]
dest[[1]] <- t(t(dest[[1]]) * counts[[2]])
return(dest)
}
Aggregate land cover at 1 km resolution
lcover_1km <- map(lcover, ~ st_warp(., uk_bbox_1km, use_gdal = TRUE, method = "average"))
lcover_1km <- map2(lcover, lcover_1km, ~ st_warp_to_sum(.x, .y))
Verify the total count per cell is complete
lcover_total <- round(Reduce("+", lcover_1km))
total_count <-
prod(sapply(st_dimensions(uk_bbox_1km), function(z) abs(z$delta))) /
prod(sapply(st_dimensions(lcover[[1]]), function(z) abs(z$delta)))
plot(lcover_total == total_count)
Each cell should sum 1600. In case it is not 1600, we will
assign the remaining count to 0
assuming that they do not correspond to any
category of interest.
lcover_1km[[1]][[1]] <- lcover_1km[[1]][[1]] + total_count - lcover_total[[1]]
lcover_total <- round(Reduce("+", lcover_1km))
unique(as.numeric(lcover_total[[1]]))
#> [1] 1600
Save land cover as a raster with bands
lcover_1km <- do.call(c, c(lcover_1km, along = 3))
write_stars(lcover_1km, file.path(path_processed, "uk_1km_landcover.tif"), type = "UInt16")
Visualize land cover at 1km resolution
lcover_1km <- read_stars(file.path(path_processed, "uk_1km_landcover.tif"), proxy = TRUE)
lcover_1km <- st_set_dimensions(lcover_1km, 3, values = classes)
plot_hook = function() plot(uk, border = 'red', add = TRUE)
plot(lcover_1km, hook = plot_hook, main = "Class", breaks = "equal" )
Time to execute the task
Only useful when executed with Rscript
.
proc.time()
#> user system elapsed
#> 292.542 70.556 371.936