
Add a layer already in the project to map themes
Source:R/rfp_qgs_theme_layer.R
rfp_qgs_theme_layer_add.RdTheme 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
.qgsproject, 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.FALSEwrites 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.
TRUEticks 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. DefaultFALSE, 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>.bakbefore mutating. DefaultTRUE.
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)