Skip to contents

Theme membership is enumerated per layer per theme, with no inheritance (#304): a Mergin theme hides a layer by ABSENCE from the preset, so a layer must be listed in every theme it should appear in. Until this existed there was no way to say that about a layer the project already holds. The only routes were to re-add the layer - which is not available once it is there - or to read every theme with rfp_qgs_themes(), edit the frame and write the whole set back with rfp_qgs_theme_set(), which regenerates every preset it touches and so puts the group and legend state of themes you did not mean to edit at risk (#121). Putting Trails into five themes on one project was done by an uncommitted ad-hoc script for exactly this reason.

Usage

rfp_qgs_theme_layer_add(
  qgs,
  layer,
  themes = "all",
  visible = TRUE,
  check_groups = FALSE,
  backup = TRUE
)

Arguments

qgs

Path to a .qgs project, or a directory holding exactly one.

layer

Character. Layer name(s) already in the project.

themes

Character. Theme names, or "all" for every theme in the project. A name the project does not have is an error - a caller naming a theme by hand has almost certainly typed it wrong.

visible

Logical, recycled over layer. FALSE writes the entry present-and-off, which is what #162 uses to keep a basemap reachable without a theme switch starting a tile fetch.

check_groups

Logical. TRUE ticks the group holding each newly visible layer, and every ancestor of it, in that preset's <checked-group-nodes> - so the layer draws rather than being listed and buried. Default FALSE, which only warns, because that is what every caller before #313 got.

Three properties are deliberate. It acts only on a preset carrying has-checked-group-info="1": an absent assertion means "group state not recorded", not "nothing checked", and writing nodes into such a preset would make it assert a state it never had. It ticks ancestors, not just the containing group, because a checked group inside an unchecked parent is still hidden. And it never unticks - a group checked for another layer's sake is left alone, so this cannot hide something it was not asked about.

backup

Logical. Write <path>.bak before mutating. Default TRUE.

Value

Invisibly, a data frame of theme, layer, action - "added", "updated", or "unchanged" where the entry already said what was asked. Nothing is written when every row is "unchanged" and no group was ticked.

Details

This is surgical: it writes one <layer> entry and its paired <expanded-legend-nodes> sibling, and touches nothing else in the preset.

Idempotent. A layer already in a theme has its visible updated rather than a second entry appended - QGIS reads a preset as a list and a duplicate id is a layer listed twice.

A layer inside an unchecked group draws nothing (#216), which is the failure that reads as "the theme is broken" rather than "a group is off". Adding a visible layer whose group the preset leaves unchecked is reported, and check_groups = TRUE ticks the group instead of only naming it (#313).

Examples

qgs <- file.path(tempdir(), "theme_layer.qgs")
file.copy(system.file("testdata", "raster_add_fixture.qgs", package = "rfp"),
          qgs, overwrite = TRUE)
#> [1] TRUE
present <- rfp_qgs_themes(qgs)$layers
head(present, 3)
#>                     theme           layer visible expanded   style
#> 1 High Detail - Crossings            Lake   FALSE     TRUE default
#> 2 High Detail - Crossings     Conservancy   FALSE     TRUE default
#> 3 High Detail - Crossings habitat_lateral   FALSE     TRUE default

# A layer the project holds but no theme lists.
absent <- setdiff(
  xml2::xml_text(xml2::xml_find_all(
    xml2::read_xml(qgs), "./projectlayers/maplayer/layername")),
  present$layer
)
if (length(absent)) {
  rfp_qgs_theme_layer_add(qgs, absent[[1]], themes = "all", backup = FALSE)
}
#>  Set 1 layer in 5 themes
#> ! 1 entry is visible in a theme that leaves the containing group unchecked, so it will draw nothing: "Wetland in High Detail - Crossings". Check the group there, or rebuild the theme with `rfp_qgs_theme_set()`. Or pass `check_groups = TRUE` to tick it here.
unlink(qgs)