Write a shipped map theme into a .qgs, so a theme is no longer a property
of whichever project happened to have it first. The counterpart of
rfp_qgs_theme_rm(), and the registry-driven sibling of
rfp_qgs_theme_set() - which stays the low-level editor for a theme you are
building or porting by hand.
Arguments
- qgs
Character. Path to the
.qgs, or to a project directory containing one.- themes
Character vector of theme names from
rfp_qgs_theme_lookup(). Defaults to every theme in the registry.- template
Character. Which template's rows to prefer.
NULL(default) reads it from the project'srfp_project.json(#118) when there is one, and otherwise uses the portable rows only.- force
Logical. Replace a theme that already exists. Default
FALSE.- on_missing_layer
One of
"warn"(default),"error","skip"- passed torfp_qgs_theme_set().- membership
Passed to
rfp_qgs_theme_set()."declared"(default) lists what the registry names;"universal"also lists every other layer the project holds, off. See that function for what universal membership costs and what it breaks.- backup
Logical. Write
<qgs>.bakbefore mutating. DefaultTRUE.
Value
Invisibly, a data frame with one row per theme in themes:
theme, action, layers, layers_missing, groups, groups_dropped.
action is "added", "skipped" (already present, and force was not
set), or "no rows" - a theme recorded per template, reaching a project of
a different one, which has nothing to apply.
An existing theme is left alone
A theme already in the project is skipped, and the skip is reported.
rfp_qgs_theme_set() regenerates a <visibility-preset> from scratch, so
anything its reader misses is destroyed on write - silently, with a summary
that looks clean (#121). A hand-curated Desktop theme is exactly what a
re-run must not flatten, so replacing one takes force = TRUE.
That also makes the default path on rfp_project_create() a no-op for the
themes a template already carries.
Layers port; groups do not
The layer half of a theme applies to any project sharing those layer names,
and a layer the project lacks drops its reference rather than leaving a
dangling id. So Imagery reaches 11 of its 13 layers in both shipped
templates and needs no per-template variant.
The group half is template-scoped. Group rows recorded against a different template are dropped before the write and reported: the theme still lands, with its group state inferred from where its layers sit rather than carried.
That last sentence was true of the documentation and false of the code until
#302. The meta frame declared group_info = TRUE for every theme, which
rfp_qgs_theme_set() reads as "this theme declared its group state, and
declared it empty" - so instead of inferring, it wrote a preset asserting
that NO group is checked. A preset carrying has-checked-group-info="1"
asserts full group state, so every group was then unchecked and QGIS hid
every layer inside one. Measured on a bcfishpass_mobile copy: Imagery
wrote 11 layers with 8 visible and Land Tenure 25 with 21, both over zero
checked groups - healthy counts, nothing drawn (#216, #217). group_info is
now per theme, so the inference branch is reachable.
See also
Other qgs:
rfp_qgs_form_add(),
rfp_qgs_layer_rm(),
rfp_qgs_preview_set(),
rfp_qgs_raster_add(),
rfp_qgs_service_add(),
rfp_qgs_service_rm(),
rfp_qgs_services(),
rfp_qgs_style_export(),
rfp_qgs_style_set(),
rfp_qgs_subset_get(),
rfp_qgs_subset_set(),
rfp_qgs_theme_lookup(),
rfp_qgs_theme_names(),
rfp_qgs_theme_rm(),
rfp_qgs_theme_set(),
rfp_qgs_theme_unbury(),
rfp_qgs_themes(),
rfp_qgs_vector_add(),
rfp_qwc_config_create(),
rfp_raster_style_path(),
rfp_raster_styles()
Examples
src <- system.file("templates", "bcrestoration_mobile.qgs", package = "rfp")
dst <- file.path(tempdir(), "themed.qgs")
file.copy(src, dst, overwrite = TRUE)
#> [1] TRUE
# the template's own themes are left alone; Imagery is new
res <- rfp_qgs_theme_add(dst, backup = FALSE)
#> ℹ 5 themes already in the project and left alone: "High Detail - Crossings", "Land Tenure", "Low Detail - Bull Trout Model", "Low Detail - Salmon Model", and "Low Detail - Steelhead Model". Use `force = TRUE` to replace them - which regenerates the preset and cannot carry curated state it does not read.
#> ℹ This project records no template, so 10 scoped keep rows were not applied. Pass `template` to carry them.
#> Warning: Theme 'Floodplain' references 3 layer(s) not in destination: floodplains.floodplain, floodplains.landcover_2023, floodplains.transition
#> Warning: Theme 'Imagery' references 2 layer(s) not in destination: Position track, Trail feature
#> ✔ Set 3 themes in /tmp/RtmpfSNDuG/themed.qgs
res[, c("theme", "action")]
#> theme action
#> 1 Floodplain added
#> 2 Floodplain - Habitat Lateral added
#> 3 High Detail - Crossings skipped
#> 4 Imagery added
#> 5 Land Tenure skipped
#> 6 Low Detail - Bull Trout Model skipped
#> 7 Low Detail - Salmon Model skipped
#> 8 Low Detail - Steelhead Model skipped
