code-update
Inputs¶
code-updateMUST read, reproject to EPSG:4326, and coverage-clean both OLD (already coded) and NEW (uncoded candidate) inputs viacore.io.read_reproject_and_clean().
Level resolution and format detection¶
code-updateMUST resolve OLD's and NEW's own per-level code/name columns independently, each via the same explicit-pair-or-structural- fallback contractcode-createuses (docs/pages/4-codes/reference/code_create.md):name_field_a/code_field_a(or structural auto-detection) for OLD,name_field_b/code_field_b(or structural auto-detection) for NEW.code-updateMUST raiseValueError("no existing code column to overwrite") if either side resolves a level with no code column at all (e.g. a trailing finest level with only a name column, seedocs/adr/0106), rather than silently skipping that level. With explicit templates, a level-0 code column MUST be left untouched, and a NEW level with a name column but no code column MUST get one seeded from its names.code-updateMUST raiseValueErrorin structural mode on either side ("group units like a level") if detection sets any column aside as a supplemental coarser grouping, and ("a coarser level merged into this one") if any member of a level's group-by has over 30% fewer values than its code under each parent, rather than coding a merged or skipped level (seedocs/adr/0121).code-updateMUST raiseValueError("level mismatch") if OLD's and NEW's resolved level counts differ, before any dissolve/classify stage runs: a real level-count change needs a human decision, not an automatic pass.code-updateMUST detectroot_code/delimiter/min_widthviacore.code.detect_code_format(), run against OLD's own resolved finest-level code column, whenever any of the three is omitted; each field independently falls back to the detected value only when that field itself isNone, an explicitly given field is never overridden by detection. An explicitmin_widthMAY be one width, a comma list with exactly one width per level (coarsest first), orauto, as incode-create; a detected one is per level when OLD's levels differ.- When OLD's finest codes contain no non-alphanumeric character, or
delimiter=''is given,code-updateMUST detect the format from OLD's per-level code columns instead (detect_undelimited_format()):root_codeas level 1's shared leading non-digit run, and one width per level from the length each level adds to its parent's code, raisingValueErroron a missing root or mixed widths.min_width='auto'with an empty delimiter MUST raiseValueError.
Dissolve¶
code-updateMUST dissolve OLD and NEW independently at every resolved level, reusingcore.dissolve's stage function directly: OLD grouped by its own already-real code column, NEW grouped by its own resolved (structural or explicit) code column.
Classify¶
code-updateMUST classify every level by reusingcore.change's own overlap and classify stage functions directly against that level's two dissolved tables, producing everyrelationship_classchangedefines:unchanged,renamed,modified,relocated,split,merge,complex,created,removed.tau_match,tau_same,link_by_code,link_by_name, andlink_modeMUST be plain passthrough tochange's own classification engine, same names and defaults (tau_match=0.8,tau_same=0.98,link_by_code=False,link_by_name=False,link_mode="either").code_column_a/code_column_b/name_column_a/name_column_b(reusingchange's own flag names) MUST default to that level's own resolved code/name columns, and MUST be independently overridable, for identity- linking a case overlap-based linking can't catch on its own (e.g. arelocatedunit).
Reparent¶
- For every level finer than the coarsest,
code-updateMUST re-derive each NEW unit's true current parent spatially against the immediately- coarser level's NEW, already-dissolved units, viacore.assign.assign_many()(per-child best overlap, not a shared majority vote); it MUST NOT trust a stale embedded parent column, since that column goes stale exactly when the coarser unit was itself split, merged, or relocated this version.
Assign (retention policy)¶
Applied per level, ascending, using the level's own already-re-derived parent codes:
relationship_class |
outcome |
|---|---|
unchanged, renamed |
code retained: rewrite_child_code() reattaches the OLD code's own tail onto the unit's (possibly new) parent prefix |
modified |
without a delimiter, code retained as for unchanged; with one, as for relocated |
relocated |
new code assigned under the re-derived parent; predecessor_code is the one linked OLD code |
created |
new code assigned; predecessor_code is NULL |
split (1 OLD to N NEW) |
each NEW unit gets its own new code; all share one predecessor_code, the one OLD code |
merge (N OLD to 1 NEW) |
the one NEW unit gets one new code; predecessor_code is NULL on that geometry row (a scalar can't hold N predecessors; full lineage lives in the changelog, see Outputs) |
complex (N OLD to M NEW) |
each NEW unit gets its own new code, one shared next-available scope per level; predecessor_code is NULL on every geometry row, same reasoning as merge |
removed |
no NEW-side output row; the OLD code is excluded from output, and no new code reuses it |
- A new code MUST be numbered above every code its parent had at that
level in OLD, retired codes included, so no OLD code is ever issued to a
different unit (see
docs/adr/0126). - A
modifiedunit MUST keep its code (outcomeretained, reasongeometry modified, code retained (lenient: codes without a delimiter)) when the format has no delimiter, and MUST get a new code when it has one. match_methodMUST be that one pair's ownchange-assigned value ("spatial"or"identity") for a cluster spanning exactly one old/new pair; for a cluster spanning multiple linked pairs (merge,complex, or asplit's per-child links), every linked pair's ownmatch_methodMUST be collapsed into one value, joined with"+"when genuinely mixed.- A
'new'-outcome row'scode_outcomeMUST be overwritten to'overflow'(reusingcode-create's own10 ** width - 1capacity rule, never underauto) when its parent's total retained-plus-new child count at that level exceeds capacity;reasonMUST be overwritten to state the overflow.
Outputs¶
code-updateMUST write each level's new code into the column OLD's own resolution named at that level, in place on the NEW-side finest table. NEW's own raw column at that level, if differently named, MUST be left untouched as an ordinary passthrough attribute.code-updateMUST add apredecessor_field(defaultpredecessor_code) column, populated only for the finest level's own rows.code-updateMUST always write a changelog, even when it would be empty, no geometry column:level,old_code,old_name,new_code,new_name,relationship_class,cluster_id,match_method,code_outcome(retained/new/retired/overflow),reason. Amergegives N retired rows plus 1 new row; acomplexcluster gives one row per actually-linked old/new pair fromchange's own pairwise table, never a full N×M cross-product; aremovedcode gives one row withnew_code=NULL; acreatedcode gives one row withold_code=NULL.code-updateperforms no topology hard gate of its own beyond whatcore.dissolve's stage function already enforces per level internally.
Configuration (api.code_update.code_update() / CLI)¶
code-updateMUST process exactly one OLD/NEW file pair per call.output_path, if omitted, MUST default tonew_pathwith a_codedstem suffix.changelog_path, if omitted, MUST default tooutput_pathwith a_changelogstem suffix and a.csvextension. It MUST be one ofcore.code.TABLE_COPY_OPTS's extensions (a tabular format; the changelog has no geometry column), raisingValueErrorotherwise.code-updateMUST raiseFileExistsErrorforoutput_pathorchangelog_pathif either already exists and overwriting wasn't requested.link_modeMUST be"either"or"both"; any other value MUST raiseValueError.step, if given, MUST be one ofinputs,levels,process,outputs; any other value MUST raiseValueError.processruns dissolve, classify, reparent, and assign for every level, one level fully before the next (seedocs/adr/0105).
Examples¶
Example 1: basic run, format auto-detected off OLD's own codes¶
topo-tools code-update admin1_old.geojson admin1_new.geojson
Example 2: identity-linking a relocated unit by source code¶
topo-tools code-update old.gpkg new.gpkg --link-by-code \
--code-column-a srcid --code-column-b srcid
Example 3: explicit output paths, custom format override¶
Writes admin1_new_coded.geojson and admin1_new_coded_changelog.csv
unless given:
topo-tools code-update old.parquet new.parquet coded.parquet changelog.csv \
--root-code AFG --delimiter . --min-width 3