Skip to content

code-refactor

code-refactor cold-starts a hierarchical code on any flat, finest-level input: no existing code column is assumed, only a hierarchy embedded as columns (the same shape package-polygons/schema-fill expect). Four stages, each a numbered module in topo_tools/core/code_refactor/.

_01_inputs: load

Loads and reprojects the one input via core.io.read_and_reproject(). code-refactor never coverage-cleans: it rewrites attribute columns only, geometry is untouched throughout the whole pipeline.

_02_levels: resolve, don't assume

Level resolution reuses package-polygons's own two-path contract rather than inventing a third: resolve_explicit_target_schema(name_field, code_field) first, and if that returns None (neither flag given), structural auto-detection (detect_level_columns_or_single() + verify_functional_cluster()) takes over. Zero levels detected raises ValueError, since a hierarchy that can't be located at all has nothing for the tool to rank. A resolved level with no code column at all (only a name, has_code=False, see docs/adr/0106) also raises ValueError: code- refactor only ever overwrites an existing column's values, it never creates one, so a codeless level needs --code-field pointed at a real column before it can be ranked at all.

Resolved levels are renumbered to a clean 1..N, coarsest first, regardless of how many raw columns existed or what they were named. A genuinely constant coarsest column (a single-country file's own admin0 code, for instance) never becomes a level; it's dropped before renumbering. This is why root_code is never stamped as its own output column: level 1 already has a real parent, the root_code string itself, so there's no level 0 for root_code to occupy. _03_assign.py's if 0 in levels: branch exists for a levels dict that could in principle carry a 0 key, but _02_levels.main() never actually produces one, so that branch never executes against real input.

_03_assign: chained, not independent, per level

Each level 1..N, ascending, is assigned via one core.code. assign_new_codes() call: rank that level's own raw values under their immediately-coarser level's already-assigned code (or the literal root_code, for level 1), and overwrite the column in place with a fresh sequential, zero-padded code.

This deliberately chains level to level, unlike package-polygons's independent per-level dissolves (each of which stands alone against the original finest table). Ranking a level's units requires that level's own parent code to already exist, so level 2 can't be assigned until level 1's own assignment has produced real parent codes to group under; there's no way to parallelize or reorder the levels the way package-polygons's independent dissolves can.

Every source value is re-ranked into a fresh integer before formatting, never passed through as-is (see docs/adr/0104): a raw hierarchy column (a GADM GID_1 like AFG.1_1, or a plain integer with gaps) is rarely clean enough to zero-pad directly, and even when it happens to look clean, nothing guarantees it's unique or gap-free across every sibling group in the file.

_04_outputs: export plus overflow reporting

The finest-level table, every resolved level's code column now overwritten in place, is exported as the main output via export_geometry_table(), same format as the input.

_write_overflow_issues() groups distinct assigned codes per level by their own parent prefix, flags any group whose count exceeds 10 ** min_width - 1, and reports the parent's own highest-tail-integer assigned code alongside the count. A run with zero overflow rows deletes any stale issues file already sitting at issues_path (.unlink(missing_ok=True)), so a re-run after fixing an overflowing input doesn't leave a misleading old report behind.

Table naming

Stages share one DuckDB connection under f"{input_basename}_code_refactor" as their own name, distinct from any code-update run against a related file (which uses its own name, scoped per NEW input), so the two tools never collide on the same tmp_dir.