Search code examples
rmetadatarastergeotiffterra

Write RasterStack and preserve metadata in R


I would like to write a RasterStack object and preserve names and metadata of the individual layers. How to preserve names is explained here. Is there a way to preserve metadata of individual layers when writing a RasterStack object? Here is replicable code:

# load library
library(raster)

# create example rasters
ras_1 <- raster(nrows=180, ncols=360, xmn=-180, xmx=180, ymn=-90, ymx=90, resolution=, vals=1)
ras_2 <- raster(nrows=180, ncols=360, xmn=-180, xmx=180, ymn=-90, ymx=90, resolution=, vals=2)
ras_3 <- raster(nrows=180, ncols=360, xmn=-180, xmx=180, ymn=-90, ymx=90, resolution=, vals=3)

# assign names
names(ras_1) <- "raster_A"
names(ras_2) <- "raster_B"
names(ras_3) <- "raster_C"

# assign metadata
metadata(ras_1) <- list("metadata_raster_A")
metadata(ras_2) <- list("metadata_raster_B")
metadata(ras_3) <- list("metadata_raster_C")

# check
ras_1
ras_2
ras_3
metadata(ras_1)
metadata(ras_2)
metadata(ras_3)

# create and check stack
raster_stack <- stack(ras_1,
                      ras_2,
                      ras_3)
raster_stack
raster_stack[[1]]
metadata(raster_stack[[1]])

# write raster stack to disk
setwd("~")

# load library
library(terra)
# create rast object
raster_stack_terr <- rast(raster_stack)
# write raster stack
terra::writeRaster(raster_stack_terr, "raster_stack_terr_test.tif")

# load and check raster stack
raster_stack_check <- stack("raster_stack_terr_test.tif")
raster_stack_check
raster_stack_check[[1]]
names(raster_stack_check[[1]])
metadata(raster_stack_check[[1]])

Use terra to preseve names according to the 3rd answer from here.

When opening the RasterStack from disk, the metadata is not preserved. See console output:

> metadata(raster_stack_check[[1]])
list()

How to preserve metadata of individual layers when writing and re-loading a RasterStack object? Thanks!


Solution

  • It does not seem like {terra} offers an equivalent to raster::metadata(). However, from my perspective, the use cases would be limited here, because you would only be able to store structured information in corresponding format-specific tags (at least, this is my understanding) when writing to disk.

    TIFF files (c.f. here) seem to offer the following tags:

    TIFFTAG_DOCUMENTNAME
    TIFFTAG_IMAGEDESCRIPTION
    TIFFTAG_SOFTWARE
    TIFFTAG_DATETIME
    TIFFTAG_ARTIST
    TIFFTAG_HOSTCOMPUTER
    TIFFTAG_COPYRIGHT
    TIFFTAG_XRESOLUTION
    TIFFTAG_YRESOLUTION
    TIFFTAG_RESOLUTIONUNIT
    

    ESRI-Grids, on the other hand, do not offer any possibilities to store metadata except for the known header and maybe the filename as far as I know.

    If you only wanted to store certain metadata with your raster object, you might as well make use of attr(r, "meta") <- "foobar". However, I don't see how this (random) information can be stored in specific formats and restored afterwards.

    You already noticed names() when using {terra}, but there is also time() to be mentioned. Maybe this already suits your needs, since you did not specify what exactly you intend to store.

    # set up a raster stack with three layers
    library(terra)
    #> terra 1.6.17
    
    # create raster
    r <- rast(nrows = 10, ncols = 10)
    values(r) <- rnorm(100)
    
    # set metadata
    names(r) <- "foo"
    time(r) <- as.Date("2000-01-01")
    attr(r, "meta") <- "bar"
    
    # inspect
    r
    #> class       : SpatRaster 
    #> dimensions  : 10, 10, 1  (nrow, ncol, nlyr)
    #> resolution  : 36, 18  (x, y)
    #> extent      : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
    #> coord. ref. : lon/lat WGS 84 
    #> source      : memory 
    #> name        :       foo 
    #> min value   : -2.503790 
    #> max value   :  1.998731 
    #> time (days) : 2000-01-01
    
    # write to disk
    writeRaster(r, "sample.tif", overwrite = TRUE)
    
    # read from disk
    r2 <- rast("sample.tif")
    r2
    #> class       : SpatRaster 
    #> dimensions  : 10, 10, 1  (nrow, ncol, nlyr)
    #> resolution  : 36, 18  (x, y)
    #> extent      : -180, 180, -90, 90  (xmin, xmax, ymin, ymax)
    #> coord. ref. : lon/lat WGS 84 (EPSG:4326) 
    #> source      : sample.tif 
    #> name        :       foo 
    #> min value   : -2.503790 
    #> max value   :  1.998731 
    #> time (days) : 2000-01-01
    
    # try to access attributes
    attr(r2, "meta")
    #> NULL
    

    As expected, data stored as attribute has been lost whereas information provided via names() and time() was sustained.