r-lidar / lidR

Airborne LiDAR data manipulation and visualisation for forestry application
https://CRAN.R-project.org/package=lidR
GNU General Public License v3.0
601 stars 131 forks source link

pixel_metrics cannot work in parallel because SpatRaster are not serializable #523

Closed Jean-Romain closed 2 years ago

Jean-Romain commented 2 years ago

Minimal reproducible example

library(lidR)

LASfile <- system.file("extdata", "Megaplot.laz", package="lidR")
ctg <- readLAScatalog(LASfile, select = "xyz", chunk_size = 140, chunk_buffer = 0)
opt_chunk_alignment(ctg) <- c(0,20)
plot(ctg, chunk = TRUE)

library(future)
plan(multisession, workers = 2)
pixel_metrics(ctg, ~max(Z), 20)
#> Valeur NULL passée comme adresse symbolique[[1]]
#> class       : SpatRaster 
#> Erreur dans x@ptr$nrow() : external pointer is not valid
#> De plus : Message d'avis :
#> An error occured during the automatic merge of 'catalog_apply'. Merging is impossible. A list has been returned. 

The problem comes from the fact that SpatRaster are not serializable and cannot be sent to nodes (or here be sent back to the main worker). As a consequence the following options are expected to work because no SpatRaster is exchanged between nodes

library(future)
plan(multisession, workers = 2)
opt_output_files(ctg) = "{tempdir()}/test_{ID}"
pixel_metrics(ctg, ~max(Z), 20)
#> class       : SpatRaster 
#> dimensions  : 13, 12, 1  (nrow, ncol, nlyr)
#> resolution  : 20, 20  (x, y)
#> extent      : 684760, 685000, 5017760, 5018020  (xmin, xmax, ymin, ymax)
#> coord. ref. : NAD83 / UTM zone 17N (EPSG:26917) 
#> source      : pixel_metrics.vrt 
#> name        :    V1 
#> min value   :  0.05 
#> max value   : 29.97 
library(future)
plan(multisession, workers = 2)
pixel_metrics(ctg, ~max(Z), 20, pkg = "raster")
#> class      : RasterLayer 
#> dimensions : 13, 12, 156  (nrow, ncol, ncell)
#> resolution : 20, 20  (x, y)
#> extent     : 684760, 685000, 5017760, 5018020  (xmin, xmax, ymin, ymax)
#> crs        : +proj=utm +zone=17 +datum=NAD83 +units=m +no_defs 
#> source     : memory
#> names      : V1 
#> values     : 0.05, 29.97  (min, max)
candelas762 commented 11 months ago

The example does not throw the error anymore but I does with my data and I found another related error and solution. In my case I was trying to use a SpaRaster as a template and was failing. The solution is to convert that raster to a RasterLayer with raster::raster().

This is a silly reproducible example to use the maximum height raster as a template for minimum heigh calculation:

library(lidR)

LASfile <- system.file("extdata", "Megaplot.laz", package="lidR")
ctg <- readLAScatalog(LASfile, select = "xyz", chunk_size = 140, chunk_buffer = 0)
opt_chunk_alignment(ctg) <- c(0,20)
plot(ctg, chunk = TRUE)

library(future)
plan(multisession, workers = 2)

m = pixel_metrics(ctg, ~max(Z), 20)
# Works
pixel_metrics(ctg, ~min(Z), m)
# Does not work
pixel_metrics(ctg, ~miin(Z), raster(m))
# Works
Jean-Romain commented 11 months ago

Honestly, I'm tired of fixing bugs coming from the fact that SpatRaster are badly designed and are not serializable. My code contains several workarounds for that to a point that I'm not even sure where they are and what they are doing. You can process single core or use Raster as a workaround as you did. Anyway, it is what I'm doing internally. Please open a new issue with your reproducible example, so I can remember to fix that one. Thanks