Skip to content

dft_rast_classify() returns empty levels when its input is already a factor #91

Description

@NewGraphEnvironment

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions