Using terra with geotargets

library(geotargets)

The geotargets package extends targets to work with geospatial data formats, such as rasters and vectors (e.g., shape files). In particular, geotargets aims to support use of the terra package, which tend to cause problems if used in targets created with tar_target() . If you are new to targets, you should start by looking at the targets manual to get a handle on the basics.

The design of geotargets is to specify target factories like so: tar_<pkg>_<type>.

In this vignette we will demonstrate the use of the terra R package, and we will demonstrate how to build raster (rast), vector (vect), raster collection (sprc), and raster dataset (sds) targets with:

  • tar_terra_rast()
  • tar_terra_vect()
  • tar_terra_sprc()
  • tar_terra_sds()

tar_terra_rast(): targets with terra rasters

# contents of _targets.R:
library(targets)
library(geotargets)
tar_option_set(packages = "terra")
geotargets_option_set(gdal_raster_driver = "COG")
list(
    tar_target(
        tif_file,
        system.file("ex/elev.tif", package = "terra"),
        format = "file"
    ),
    tar_terra_rast(
        r,
        {
            rast <- rast(tif_file)
            units(rast) <- "m"
            rast
        }
    ),
    tar_terra_rast(
        r_agg,
        aggregate(r, 2)
    )
)

Above is a basic example showing the use of tar_terra_rast() in a targets pipeline. The command for tar_terra_rast() can be any function that returns a SpatRaster object. In this example, we’ve set the output to a cloud optimized geotiff (“COG”), but any GDAL driver that works with terra::writeRaster() should also work here. You can also set this option on a target-by-target basis with the filetype argument to tar_terra_rast().

Running the pipeline:

tar_make()
#> ▶ dispatched target tif_file
#> ● completed target tif_file [0.011 seconds, 7.994 kilobytes]
#> ▶ dispatched target r
#> ● completed target r [0.003 seconds, 10.433 kilobytes]
#> ▶ dispatched target r_agg
#> ● completed target r_agg [0.002 seconds, 6.303 kilobytes]
#> ▶ ended pipeline [0.131 seconds]
tar_read(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      : r 
#> name        : elevation 
#> min value   :       141 
#> max value   :       547
tar_read(r_agg)
#> class       : SpatRaster 
#> dimensions  : 45, 48, 1  (nrow, ncol, nlyr)
#> resolution  : 0.01666667, 0.01666667  (x, y)
#> extent      : 5.741667, 6.541667, 49.44167, 50.19167  (xmin, xmax, ymin, ymax)
#> coord. ref. : lon/lat WGS 84 (EPSG:4326) 
#> source      : r_agg 
#> name        : elevation 
#> min value   :    166.75 
#> max value   :    529.50

Raster metadata

You may have noticed that the units for the r target above have gone missing. This is due to limitations of terra and targetsterra saves some metadata in “sidecar” aux.json files and targets enforces a strict one file per target rule. You can get around this by setting preserve_metadata = "zip" in tar_terra_rast() to save the output files, including the metadata, as a minimally compressed zip archive. You can also set this for all raster targets with geotargets_option_set(terra_preserve_metadata = "zip").

Note: there are likely performance costs associated with this option.
As an alternative, you can encode information in the layer names by setting names(r) <- which are retained even with the default preserve_metadata = "drop".

# contents of _targets.R:
library(targets)
library(geotargets)
tar_option_set(packages = "terra")
geotargets_option_set(gdal_raster_driver = "COG")
list(
    tar_target(
        tif_file,
        system.file("ex/elev.tif", package = "terra"),
        format = "file"
    ),
    tar_terra_rast(
        r,
        {
            rast <- rast(tif_file)
            units(rast) <- "m"
            rast
        },
        preserve_metadata = "zip"
    )
)
tar_make()
#> ✔ skipped target tif_file
#> ▶ dispatched target r
#> ● completed target r [0.004 seconds, 10.252 kilobytes]
#> ▶ ended pipeline [0.126 seconds]
terra::units(tar_read(r))
#> [1] "m"

tar_terra_vect(): targets with terra vectors

For terra SpatVector objects, use tar_terra_vect() in the pipeline. You can set vector specific options with geotargets_option_set() or with the filetype and gdal arguments to individual tar_terra_vect() calls.

# contents of _targets.R:
library(targets)
library(geotargets)
geotargets_option_set(gdal_vector_driver = "GeoJSON")
list(
    tar_target(
        vect_file,
        system.file("ex", "lux.shp", package = "terra"),
        format = "file"
    ),
    tar_terra_vect(
        v,
        terra::vect(vect_file)
    ),
    tar_terra_vect(
        v_proj,
        terra::project(v, "EPSG:2196")
    )
)
tar_make()
#> ▶ dispatched target vect_file
#> ● completed target vect_file [0 seconds, 64.692 kilobytes]
#> ▶ dispatched target v
#> ● completed target v [0.008 seconds, 117.605 kilobytes]
#> ▶ dispatched target v_proj
#> ● completed target v_proj [0.016 seconds, 213.51 kilobytes]
#> ▶ ended pipeline [0.132 seconds]
tar_read(v)
#>  class       : SpatVector 
#>  geometry    : polygons 
#>  dimensions  : 12, 6  (geometries, attributes)
#>  extent      : 5.74414, 6.528252, 49.44781, 50.18162  (xmin, xmax, ymin, ymax)
#>  source      : v
#>  coord. ref. : lon/lat WGS 84 (EPSG:4326) 
#>  names       :  ID_1   NAME_1  ID_2   NAME_2  AREA   POP
#>  type        : <num>    <chr> <num>    <chr> <num> <int>
#>  values      :     1 Diekirch     1 Clervaux   312 18081
#>                    1 Diekirch     2 Diekirch   218 32543
#>                    1 Diekirch     3  Redange   259 18664
tar_read(v_proj)
#>  class       : SpatVector 
#>  geometry    : polygons 
#>  dimensions  : 12, 6  (geometries, attributes)
#>  extent      : -69990.51, -13879.85, 5484907, 5566555  (xmin, xmax, ymin, ymax)
#>  source      : v_proj
#>  coord. ref. : ETRS89 / Kp2000 Jutland (EPSG:2196) 
#>  names       :  ID_1   NAME_1  ID_2   NAME_2  AREA   POP
#>  type        : <num>    <chr> <num>    <chr> <num> <int>
#>  values      :     1 Diekirch     1 Clervaux   312 18081
#>                    1 Diekirch     2 Diekirch   218 32543
#>                    1 Diekirch     3  Redange   259 18664

tar_terra_sprc(): targets with terra raster collections

Targets that produce a SpatRasterCollection can be created with tar_terra_sprc(). The various rasters in the collection are saved as subdatasets to adhere to targets one file per target rule.

# contents of _targets.R:
library(targets)
library(geotargets)
elev_scale <- function(raster, z = 1, projection = "EPSG:4326") {
    terra::project(
        raster * z,
        projection
    )
}
tar_option_set(packages = "terra")
geotargets_option_set(gdal_raster_driver = "GTiff")
list(
    tar_target(
        elev_file,
        system.file("ex", "elev.tif", package = "terra"),
        format = "file"
    ),
    tar_target(
        r,
        rast(elev_file)
    ),
    tar_terra_sprc(
        raster_elevs,
        # two rasters, one unaltered, one scaled by factor of 2 and
        # reprojected to interrupted Goode homolosine
        terra::sprc(list(
            elev_scale(r, 1),
            elev_scale(r, 2, "+proj=igh")
        ))
    )
)
tar_make()
#> ▶ dispatched target elev_file
#> ● completed target elev_file [0.011 seconds, 7.994 kilobytes]
#> ▶ dispatched target r
#> ● completed target r [0.003 seconds, 959.075 kilobytes]
#> ▶ dispatched target raster_elevs
#> ● completed target raster_elevs [0.055 seconds, 37.904 kilobytes]
#> ▶ ended pipeline [1.073 seconds]
tar_read(raster_elevs)
#> class       : SpatRasterCollection 
#> length      : 2 
#> nrow        : 90, 115 
#> ncol        : 95, 114 
#> nlyr        :  1,   1 
#> extent      : 5.741667, 1558890, 49.44167, 5556741  (xmin, xmax, ymin, ymax)
#> crs (first) : lon/lat WGS 84 (EPSG:4326) 
#> names       : raster_elevs, raster_elevs

tar_terra_sds(): targets with terra raster datasets

A terra SpatRasterDataset is very similar to a SpatRasterCollection except that all sub-datasets must have the same projection and extent

# contents of _targets.R:
library(targets)
library(geotargets)
tar_option_set(packages = "terra")
list(
    tar_target(
        logo_file,
        system.file("ex/logo.tif", package="terra"),
        format = "file"
    ),
    tar_terra_sds(
        raster_dataset,
        {
            x <- sds(rast(logo_file), rast(logo_file)/2)
            names(x) <- c("first", "second")
            x
        }
    )
)
tar_make()
#> ▶ dispatched target logo_file
#> ● completed target logo_file [0.011 seconds, 22.458 kilobytes]
#> ▶ dispatched target raster_dataset
#> ● completed target raster_dataset [0.03 seconds, 54.735 kilobytes]
#> ▶ ended pipeline [0.136 seconds]
tar_read(raster_dataset)
#> class       : SpatRasterDataset 
#> subdatasets : 2 
#> dimensions  : 77, 101 (nrow, ncol)
#> nlyr        : 3, 3 
#> resolution  : 1, 1  (x, y)
#> extent      : 0, 101, 0, 77  (xmin, xmax, ymin, ymax)
#> coord. ref. : Cartesian (Meter) 
#> source(s)   : raster_dataset 
#> names       : raster_dataset, raster_dataset