Skip to contents

Generates a field form's two artifacts from its git-diffable schema CSV: an empty typed GeoPackage and a QGIS .qml sidecar carrying the field aliases, edit widgets, tabbed form layout and symbology. This is the maintainer-side counterpart to rfp_form_create(), which copies the already-built GeoPackage into a project.

Usage

rfp_form_build(
  type,
  path_out,
  schema = NULL,
  layer = NULL,
  dir_photos = "ignore_mobile/photos",
  crs = 3005,
  symbol = NULL,
  color = NULL,
  label_expression = NULL,
  on_records = c("stop", "migrate", "overwrite")
)

Arguments

type

Character. Form type, e.g. "vri_qa". Must be registered in rfp_form_types.csv with a schema_source. See rfp_form_types().

path_out

Character. Output path for the form GPKG. The QML is written beside it with the same basename.

schema

Character. Optional path to a schema CSV, overriding the registry's schema_source. Use when drafting a form that is not registered yet.

layer

Character. Layer name inside the GPKG. Defaults to form_<type>.

dir_photos

Character. Project-relative directory photo widgets store into. Default "ignore_mobile/photos", matching the existing rfp and dff-2022 forms.

crs

Numeric. EPSG code for the form layer. Default 3005 (BC Albers).

symbol

Character. Optional symbol template name, overriding the registry. See inst/extdata/forms/symbols/.

color

Character. Optional hex colour (e.g. "#E31AC8"), overriding the registry.

label_expression

Character. Optional QGIS label expression, overriding the registry's label_expression. "" labels nothing.

on_records

Character. What to do when path_out already holds records and the schema has changed - which replaces the whole file. "stop" (the default) refuses, naming the row count. "migrate" carries the records into the new schema, matching on column name, leaving added columns null and reporting anything dropped. "overwrite" replaces the file and its records. Ignored when the schema is unchanged, since nothing is written.

Value

Invisible character - the path to the GPKG.

Details

Everything a form needs is data, never code. Per-field configuration (type, widget, tab, label, dropdown options, default) lives in the schema CSV; per-form configuration (schema path, geometry, symbol, colour) lives in the registry read by rfp_form_types(). Adding a form, or restyling one, should require no change to this function.

The GeoPackage is only rewritten when its columns, geometry type or CRS actually differ from the schema. An unconditional write would churn a ~100 KB binary in git on every rebuild, because GeoPackage embeds a creation timestamp. The QML is always rewritten - it is text, and byte-stable for a given schema.

Rebuilding replaces the whole file rather than altering the layer in place, so when the schema has changed and path_out holds anything else - another layer, or the layer_styles table QGIS writes - the build stops rather than discarding it.

The same replacement would destroy the form's own records. A deployed form is not only a build artifact: once rfp_form_create() has put it in a project and Mergin has synced it, it is the surface a field crew captures onto. So a schema change over a form that holds records stops by default, and on_records says what to do instead - carry the records into the new schema, or discard them deliberately. A form with no records rebuilds silently either way, which is every form rfp ships (#49).

See also

rfp_form_types() for the registry, rfp_form_create() to deploy a built form into a project.

Other form: rfp_form_create(), rfp_form_types()

Examples

# Build the land cover transition QA form into a temporary directory
out <- file.path(tempdir(), "form_transition_qa.gpkg")
rfp_form_build("transition_qa", out)
#>  Built transition_qa form: /tmp/RtmpfSNDuG/form_transition_qa.gpkg (20 fields, /tmp/RtmpfSNDuG/form_transition_qa.qml)

# The typed, empty layer...
sf::st_layers(out)
#> Driver: GPKG 
#> Available layers:
#>           layer_name geometry_type features fields          crs_name
#> 1 form_transition_qa         Point        0     20 NAD83 / BC Albers

# ...and its QML sidecar, carrying widgets, tabs and symbology
qml <- sub("\\.gpkg$", ".qml", out)
length(xml2::xml_find_all(xml2::read_xml(qml), "//attributeEditorContainer"))
#> [1] 3

unlink(c(out, qml))