Skip to content

Feed crop/livestock spatialize the fractional cell-coverage grid, not the centroid grid #461

Description

@lbm364dl

Important

Delivered by the polycell work on branch edu/polycell-spatial-support (@eduaguilera) — the consumer-side half of #460. Do not start.

The "decision needed" in this issue is settled: polycell_id, not majority-fraction overwrite. Majority-fraction is centroid assignment under another name — it re-creates the very failure this issue opens with (a polity smaller than a cell gets nothing), just with a different tie-break. The polycell costs +6.3% rows (68,527 vs 64,438), which buys the guarantee that aggregating polycells to a polity changes no absolute value and no quantity crosses a border it does not belong to.

Corollary, and a separate acceptance criterion: that guarantee only holds if every allocation is keyed on the polycell, never computed on the cell and apportioned afterwards. Computing from cell-average drivers and splitting after the fact hands a neighbour a share of whatever the cell produced.

Measurements from that work: the centroid pin is genuinely one polity per cell (58,791 rows = 58,791 unique cells, no share column, so .normalize_country_grid() defaults cell_area_frac = 1). The two grids disagree in both directions — 5,651 cells in the fractional grid and not the centroid one, and 4 cells the other way ((-75.75, 22.25) a/c 12, (98.25, 11.25) a/c 28, (131.75, -4.75) a/c 101, (-80.75, -33.75) a/c 40) which are carbon-path rows today, so a naive switch silently deletes them. Small polities are worse than "zero cells": French Polynesia 18 polycells, Kiribati 9, Micronesia 4, Maldives 1, all with zero LUH2 terrestrial area, so each draws a full cell and over-counts land by roughly 13×, 34×, 18× and 10×.

This will expose polygon-quality problems underneath itMDV-1800-2025 measures 24.4 km² against an official 300 km², MCO-1800-2025 19.7 km² against 2.08 km². Those belong upstream in whep-polities, not here.

Two wiring traps for whoever touches it: landfrac is already an alias for cell_area_frac in .normalize_country_grid(), so a land-fraction column under that name is silently reinterpreted as a polity share with no warning and no failing test; and inst/scripts/run_spatialize.R:255-258 and :396-403 conditionally re-attach the centroid grid on (lon, lat), which becomes many-to-many under a fractional or polycell grid and will not warn because the next join already declares relationship = "many-to-many".

Original text follows for the record.


Problem
prepare_spatialize_all.R builds TWO grids from the same shapefile: prepare_country_grid()
(Section 1) does single-centroid terra::rasterize() — one area_code per 0.5° cell, winner-take-
all at borders — and this is what build_gridded_landuse()/build_gridded_livestock() actually
consume via the spatialize-country-grid pin. build_cell_polity_fraction() (Section 1b) does the
geometrically correct thing (6x finer, per-cell fractional coverage) but that output is saved
separately as cell_polity_fraction and only read by build_cell_polity() (#460's target) —
never plumbed into the crop/livestock engine. The engine already has first-class support for
exactly this fractional input (cell_area_frac in .normalize_country_grid(), exercised by
tests/testthat/test_spatialize.R:564 and test_spatialize_livestock.R:383), so this is a wiring
gap, not a missing feature. Small polities whose land area is smaller than a 0.5° cell and whose
polygon never contains a cell centroid get ZERO cells and their entire harvested-area/livestock
total is unallocated.

Evidence

  • inst/scripts/prepare_spatialize_all.R:887-933 (prepare_country_grid, centroid),
    :945-1027 (build_cell_polity_fraction, fractional, unused by the engine).
  • R/spatialize_compartments.R:3-34 (.normalize_country_grid already treats polity_frac as an
    alias for cell_area_frac).
  • .load_landuse_inputs()/.load_livestock_inputs() (R/run_spatialize.R:472-597) read
    spatialize-country-grid (centroid), not cell_polity_fraction.

Target state
Feed build_gridded_landuse()/build_gridded_livestock() the fractional table (or a table
unioning it with polycell_id/cell_id per compartment) as country_grid — a data-wiring change
in R/run_spatialize.R and the pin-build step, not an engine change.

Acceptance criteria

  • Small polities previously invisible to .warn_unallocated_crops()/.warn_unallocated_livestock()
    now receive nonzero cells.
  • Total spatialized area/heads per country diffed against pre-change output; document any material
    shifts.

Depends on: none. Decision needed: whether to add polycell_id (multiple compartment rows
per fractional cell, more correct) vs. overwrite with the majority-fraction polity (simpler, no
row-count change).

Moves published values: yes. Port from #382: no.



Part of the polity migration epic #458.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:regionsRegions, countries, polities: harmonization & modelingarea:spatializeSpatial disaggregationneeds-expertEmbeds a methodological/scientific decision. Do not merge on green CI alone; tag an expert.priority:mediumThis is something to keep in mind

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions