Problem
dft_rast_classify() returns a raster with zero levels and no colour table when its input is already a factor. It raises no error and no warning.
r <- terra::rast(system.file("extdata", "example_2017.tif", package = "drift"))
cl <- dft_rast_classify(r * 1L, source = "io-lulc")
again <- dft_rast_classify(cl, source = "io-lulc")
nrow(terra::cats(again)[[1]]); terra::has.colors(again) # 0 FALSE
The cause is present_codes <- terra::unique(x)[, 1] (R/dft_rast_classify.R:44). On a factor, terra::unique() returns the labels ("Water", "Trees", ...), not the codes. class_table$code %in% present_codes then matches nothing, so ct is empty and both setters are given empty tables.
This is the ordinary case, not a contrived one. The published floodplain rasters are factors: stac-floodplains-bc/bulk_co_ff04/classified_2017.tif carries a RAT with a class_name column and a palette. Classifying one of those, for example to apply a remap =, returns a factor raster with no categories. Found in #89's BULK scale check, where the output had neither levels nor colours on main and on the branch alike.
Fix
Take the present codes from the raw values, not from unique() on the factor: for example terra::unique(x, as.raster = FALSE) on a level-stripped copy, or terra::freq(x, bylayer = FALSE) with value as codes. Add a test that re-classifies a classified raster and a test on a raster that carries its own RAT. Check whether the remap = path (terra::classify() on a factor) has the same shape.
Related: #19, which concerns factor input to dft_rast_transition().
Resolution (PR #94)
- The
remap = path: a remap that matches a class already worked, because terra::classify() reads raw codes and returns a plain raster. A remap that matches nothing skips classify(), so it had the same bug. Both now work.
- Fix as landed: the factor's levels are stripped on a copy with the existing
strip_copy(), after the remap, then unique() reads codes. It is guarded by is.factor(x)[1], because a bare is.factor() errors on a multi-layer stack. main classified a stack's layer 1, and that is kept.
- Cost on BULK
classified_2017.tif: file-backed peak unchanged (1.05 GiB). An in-memory factor with no matching remap pays one extra copy: 5.47 vs 4.20 GiB.
Problem
dft_rast_classify()returns a raster with zero levels and no colour table when its input is already a factor. It raises no error and no warning.The cause is
present_codes <- terra::unique(x)[, 1](R/dft_rast_classify.R:44). On a factor,terra::unique()returns the labels ("Water","Trees", ...), not the codes.class_table$code %in% present_codesthen matches nothing, soctis empty and both setters are given empty tables.This is the ordinary case, not a contrived one. The published floodplain rasters are factors:
stac-floodplains-bc/bulk_co_ff04/classified_2017.tifcarries a RAT with aclass_namecolumn and a palette. Classifying one of those, for example to apply aremap =, returns a factor raster with no categories. Found in #89's BULK scale check, where the output had neither levels nor colours onmainand on the branch alike.Fix
Take the present codes from the raw values, not from
unique()on the factor: for exampleterra::unique(x, as.raster = FALSE)on a level-stripped copy, orterra::freq(x, bylayer = FALSE)withvalueas codes. Add a test that re-classifies a classified raster and a test on a raster that carries its own RAT. Check whether theremap =path (terra::classify()on a factor) has the same shape.Related: #19, which concerns factor input to
dft_rast_transition().Resolution (PR #94)
remap =path: a remap that matches a class already worked, becauseterra::classify()reads raw codes and returns a plain raster. A remap that matches nothing skipsclassify(), so it had the same bug. Both now work.strip_copy(), after the remap, thenunique()reads codes. It is guarded byis.factor(x)[1], because a bareis.factor()errors on a multi-layer stack.mainclassified a stack's layer 1, and that is kept.classified_2017.tif: file-backed peak unchanged (1.05 GiB). An in-memory factor with no matching remap pays one extra copy: 5.47 vs 4.20 GiB.