Skip to contents

Write layer styling into a spatial data source so QGIS picks it up on layer open, with no .qgs project needed. For GeoPackage/SQLite the style goes into the layer_styles table; for file-based formats (GeoJSON, Shapefile, KML) a .qml sidecar is written beside the file.

Usage

rfp_styles_apply(
  path,
  qgs = NULL,
  qml = NULL,
  registry = NULL,
  layers = NULL,
  xref_layers = NULL,
  style_name = "default",
  image = NULL
)

Arguments

path

Character. Path to a spatial data file (GPKG, GeoJSON, Shapefile, KML) or a directory containing them. Directories are searched recursively, so the one-GeoPackage-per-layer layout works.

qgs

Character. Path to a .qgs project to take styles from.

qml

Character path to a directory of .qml files, or a data frame with table and path_qml columns.

registry

List. A gq registry object (e.g. from gq::gq_reg_main()), or TRUE to load the main registry explicitly.

layers

Character vector. Which layers to style. NULL (default) styles every layer with a matching style.

xref_layers

Named character vector mapping source layer names to target table names. Names are layer names in the style source, values are table names in the data.

style_name

Character. Name recorded for the style. Default "default". Ignored for qgs, where the QGIS layer name is used so a table styled several ways keeps each style distinguishable.

image

Character or NULL. Docker image for headless QGIS, used only by the registry path. NULL (default) uses the digest rfp pins, so the styles written are reproducible rather than following whatever :latest was last re-pushed. Pass a reference to override.

The pinned image is amd64-only, so rfp adds --platform for it; an image rfp knows nothing about is run without one, and you own its platform.

Value

Invisibly, a tibble of what was written - table, file, style_name - for the qgs and qml paths. The registry path instead returns path invisibly: the writing happens inside the container, and reconstructing the same tibble would mean parsing that container's stdout, which is exactly the brittle signal this package avoids elsewhere.

Details

Three sources of style, in order of fidelity:

qgs

Lift the styles straight out of a QGIS project. Lossless - the QML QGIS writes for a layer is that layer's project node minus its source binding, so symbology, labelling, diagrams and form config all come across verbatim. Pure R; no Docker. See rfp_qgs_style_export().

qml

A directory of .qml files, or a data frame as returned by rfp_qgs_style_export(). For a directory the file stem names the target table.

registry

Rebuild symbols from the gq cartographic registry via headless PyQGIS in Docker. Approximate - the registry is a cross-backend abstraction extracted from QGIS to drive tmap and mapgl, so styling QGIS from it is a lossy round-trip: it models one symbol layer and about twenty properties, carries no labelling, and drops per-class dash patterns. Use it only for layers no .qgs styles.

Supplying none of the three keeps the historical behaviour and uses the gq registry.

Examples

# Build a GeoPackage holding the two tables the bundled fixture styles
gpkg <- file.path(tempdir(), "styles_demo.gpkg")
pt <- sf::st_sf(
  id = 1L,
  geom = sf::st_sfc(sf::st_point(c(1470000, 900000)), crs = 3005)
)
sf::st_write(pt, gpkg, layer = "form_exceedance", quiet = TRUE)
sf::st_write(pt, gpkg, layer = "roads_dra", quiet = TRUE,
             append = TRUE)

# Style it straight from a QGIS project - no Docker, no .qgs at run time
qgs <- system.file("testdata", "styles_fixture.qgs", package = "rfp")
rfp_styles_apply(gpkg, qgs = qgs)
#>  Exported 2 styles to: /tmp/RtmpfSNDuG/rfp_styles_1c08605dfde8
#>  Applied 2 styles to 1 file

# QGIS now reads the styling from the file itself
con <- DBI::dbConnect(RSQLite::SQLite(), gpkg)
DBI::dbGetQuery(
  con, "SELECT f_table_name, styleName, length(styleQML) AS qml_bytes
          FROM layer_styles"
)
#>      f_table_name       styleName qml_bytes
#> 1 form_exceedance form_exceedance     75624
#> 2       roads_dra       roads_dra     98722
DBI::dbDisconnect(con)