
Add Editable Fields to a Position Tracking Layer
Source:R/rfp_tracking.R
rfp_tracking_fields_add.RdA track recorded by Mergin Maps is anonymous: the layer carries when, how far and who walked it, and nothing that says what the session was - a reach walk, a bushwhack, the drive in. This adds the columns that let the person who walked it name it in the field, rather than reconstructing it off a map weeks later.
Details
The GeoPackage half. rfp_qgs_tracking_fields_add() is the project half, and
both are needed: a column with no widget is invisible in the app.
| field | widget | |
track_name | text | what the session was |
track_type | value map | reach walk, bushwhack, access route, drive, other |
named_by | text | defaults to @mergin_username - who named it, where that differs from who walked it |
Why adding a column is safe
The schema is fixed by the app, so augmenting it looks reckless. Measured against the installed plugin (Mergin 2024.2):
create_tracking_layer()has exactly one invocation, insideProjectSettings.setup_tracking(), and it is reached only when the project'sPositionTracking/TrackingLayerentry is empty or names a layer the project does not have.rfp_qgs_tracking_add()writes that entry, so the branch is unreachable for an rfp project - opening the plugin's settings dialog takes the other arm, which sets layer flags and returns.Even on that branch nothing is overwritten:
get_unique_filename()would writetracking_layer_1.gpkgbeside the existing file.setup_tracking_layer()assigns defaults throughindexFromName(), one lookup per known field. It never enumerates or prunes, so an extra column is invisible to it.
That claim covers the desktop plugin, which is the only half readable from
here; the mobile app is a separate codebase. It is also a moving target, so
test-rfp_tracking.R asserts the call-site count against the installed
plugin's own source rather than against this paragraph - an upstream release
that added a reconcile path would take these columns, and everything typed
into them, silently.
What it will not do
ALTER TABLE ... ADD COLUMN only. A column that already exists is skipped, so
the call is idempotent and safe to re-run against a project in the field. A
column that exists with a different declared type is an error rather than
a repair: SQLite has no ALTER COLUMN TYPE, and the only route through is a
table rebuild that destroys the rtree index, its 18 triggers, and every
recorded session.
See also
rfp_qgs_tracking_fields_add() for the project half.
Other tracking:
rfp_qgs_tracking_add(),
rfp_qgs_tracking_fields_add(),
rfp_qgs_tracking_get(),
rfp_qgs_tracking_set(),
rfp_tracking_layer_create()
Examples
p <- file.path(tempdir(), "tracking_layer.gpkg")
rfp_tracking_layer_create(p, overwrite = TRUE)
rfp_tracking_fields_add(p)
#> ✔ Added 4 fields to "tracking_layer": track_name, track_type, track_description, and named_by
names(sf::st_read(p, quiet = TRUE))
#> [1] "tracking_start_time" "tracking_end_time" "total_distance"
#> [4] "tracked_by" "track_name" "track_type"
#> [7] "track_description" "named_by" "geom"
# Idempotent - a second call adds nothing.
rfp_tracking_fields_add(p)
#> ℹ All 4 tracking fields already present.