spatpersist creates persistent identifiers for polygon
units observed over time. It is designed for datasets in which names,
labels, and boundaries may change and reliable longitudinal IDs do not
already exist.
Identity is determined from configurable spatial continuity rules. Names and other descriptive attributes are preserved in the result but are not used for matching.
spatpersistis under active development. Review transition diagnostics and validation results before using generated IDs in analysis.
Install the development version from GitHub:
# install.packages("remotes")
remotes::install_github("emre-cebeci/spatpersist")library(spatpersist)
polygons <- example_units()
result <- persist_ids(
polygons,
time = "year",
threshold = 0.75,
metric = "share_old"
)
sf::st_drop_geometry(result)[
, c(
"year",
"name",
"spatial_id",
"spatial_version_id",
"lineage_id",
"parent_id",
"transition_type"
)
]The result remains an sf object in its original row
order.
| Column | Meaning |
|---|---|
spatial_id |
One continuing spatial identity. It occurs at most once per time period. |
spatial_version_id |
A consecutive boundary spell within a spatial_id. |
lineage_id |
A broader family connecting related identities through splits, mergers, and replacements. |
parent_id |
The strongest predecessor when a related observation receives a new identity. |
transition_type |
initial, continuation, new,
split, merger, replacement, or
complex. |
Selected links also receive match_score,
match_confidence, and match_ambiguous.
Registry recovery is recorded in registry_matched.
Identifiers are dataset-local, not globally unique. Independent
projects can both contain values such as SID000001;
persistence is guaranteed only within one dataset and its registry
chain. Add a project-specific key when combining outputs from unrelated
datasets.
The core identity rule is controlled by:
metric: "share_old",
"share_new", or "iou";threshold: minimum accepted value of that metric;match_rule: greedy one-to-one matching or stricter
mutual-best matching;ambiguity_tolerance: the score difference treated as a
near tie;ambiguity_action: flag, reject, or stop on ambiguous
candidates.For example, this rejects near ties instead of selecting one deterministically:
conservative <- persist_ids(
polygons,
time = "year",
metric = "iou",
threshold = 0.75,
match_rule = "mutual_best",
ambiguity_action = "new"
)event_threshold, lineage_threshold, and
version_threshold separately control event detection,
lineage links, and geometry-version changes.
Every positive-area overlap considered by the matching engine is available as a regular data frame:
transitions <- id_transitions(result)
lineages <- id_lineages(result, time = "year")
issues <- validate_ids(result, time = "year")
nrow(issues) # zero means all implemented checks passedDiagnostics distinguish spatial eligibility, rule eligibility, ambiguity, mutual-best status, selection, confidence, and lineage membership.
plot_lineage(
result,
time = "year",
lineage_id = "LID000004"
)Solid edges are selected identity continuations. Dashed edges are additional lineage relationships, such as branches created by a split or merger.
A previous result can be supplied as an optional registry. Matching same-time geometries retain their issued IDs, while new identities are allocated above the registry’s existing range.
updated_result <- persist_ids(
updated_polygons,
time = "year",
registry = result
)Conflicting registry identity or geometry-version evidence stops
reconciliation rather than silently renumbering identities. Lineages are
different: if new spatial evidence connects observations anchored to
multiple registry lineages, the complete connected component is
consolidated under the lexicographically smallest existing
lineage_id.
sf object containing only POLYGON or
MULTIPOLYGON geometries.Invalid polygons can be repaired explicitly with
geometry_action = "repair". Optional geometry precision and
maximum time-gap controls are also available.
max_time_gap prevents a link.parent_id cannot encode every predecessor in a
merger; use id_transitions() for complete many-to-many
evidence.inst/benchmarks; performance still depends strongly on
polygon complexity and the number of candidate intersections.See vignette("spatpersist-workflow") for a complete
walkthrough.
Bug reports and contributions are welcome. Please use only synthetic or public data in issues, examples, and pull requests; do not upload confidential or restricted datasets. See CONTRIBUTING.md for the development and reporting workflow.