# spatpersist

[![R-CMD-check](https://github.com/emre-cebeci/spatpersist/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/emre-cebeci/spatpersist/actions/workflows/R-CMD-check.yaml)
[![test-coverage](https://github.com/emre-cebeci/spatpersist/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/emre-cebeci/spatpersist/actions/workflows/test-coverage.yaml)

`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.

> `spatpersist` is under active development. Review transition diagnostics and
> validation results before using generated IDs in analysis.

## Installation

Install the development version from GitHub:

```r
# install.packages("remotes")
remotes::install_github("emre-cebeci/spatpersist")
```

## Quick start

```r
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.

## Output identifiers

| 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.

## Matching controls

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:

```r
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.

## Audit and validate

Every positive-area overlap considered by the matching engine is available as
a regular data frame:

```r
transitions <- id_transitions(result)
lineages <- id_lineages(result, time = "year")
issues <- validate_ids(result, time = "year")

nrow(issues) # zero means all implemented checks passed
```

Diagnostics distinguish spatial eligibility, rule eligibility, ambiguity,
mutual-best status, selection, confidence, and lineage membership.

## Plot a lineage

```r
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.

## Preserve IDs across reruns

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.

```r
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`.

## Input requirements

- An `sf` object containing only `POLYGON` or `MULTIPOLYGON` geometries.
- One observation per spatial unit and time period.
- No exact coextensive geometries within the same time period. Geometry-only
  matching cannot distinguish their identities reproducibly, so they are
  rejected with an error.
- A non-missing, sortable time column.
- Valid, non-empty geometries with positive area.
- A consistent CRS across current data and any registry.

Invalid polygons can be repaired explicitly with
`geometry_action = "repair"`. Optional geometry precision and maximum time-gap
controls are also available.

## Current scope

- Matching is spatial and one-to-one; names do not determine identity.
- Consecutive *observed* periods are compared unless `max_time_gap` prevents a
  link.
- A single `parent_id` cannot encode every predecessor in a merger; use
  `id_transitions()` for complete many-to-many evidence.
- A reproducible scalability benchmark is included under
  `inst/benchmarks`; performance still depends strongly on polygon complexity
  and the number of candidate intersections.
- The package contains no bundled administrative datasets or domain-specific
  assumptions.

See `vignette("spatpersist-workflow")` for a complete walkthrough.

## Contributing

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](https://github.com/emre-cebeci/spatpersist/blob/main/CONTRIBUTING.md) for the development
and reporting workflow.
