
Read the cell-to-polity country grid at a containment depth
Source:R/spatialize_levels.R
read_level_country_grid.RdThe spatialization engines allocate a national total into the compartments
of a country grid: one row per physical 0.5-degree cell and territorial
unit, carrying that unit's share of the cell. level = 0L is today's grid,
one row per cell and reporting area_code. level >= 1L resolves the
polycell support one containment step deeper, so a cell held by a province
arrives as a province-keyed row instead of being folded into its country.
Under grid_vintage = "snapshot_2015" level 0 is served by the unchanged
.read_polycell_country_grid() path, so such a run is bit-for-bit what it
was. Deeper levels bypass that path entirely – in particular they never
reach .carbon_support_to_area_code(), whose fold is what deletes a
province.
Arguments
- level
Containment depth, a non-negative whole number.
0L(default) is today's cell-to-area_codegrid.- support
Polycell support table in the
build_polycell_support()grain, overridingread_polycell_support(). Not read atlevel = 0Lwithgrid_vintage = "snapshot_2015", which resolves the support itself.- containment
Containment edge table in the polity_containment schema, overriding the packaged one. Only read at
level >= 1L.- reference_year
Optional year to snapshot the grid at, applied with the package's own validity predicate.
NULL(default) keeps the interval grain, which is what the engines' per-year filter expects. Not read atlevel = 0Lwithgrid_vintage = "snapshot_2015", whose reference year is fixed at.carbon_support_year().- grid_vintage
Which vintage of the cell-to-polity support a level-0 grid is read at,
"year_aware"(default) or"snapshot_2015". See Which vintage of the support level 0 is read at, which states what each one allocates into. Not read atlevel >= 1L, which is year-aware by construction; snapshot a depth withreference_year.- containers
Optional integer vector of container reporting
area_codes the depth read is scoped to – the containers a run grants a depth.NULL(default) reads every admitted edge. Only read atlevel >= 1L. See Which containers a depth is read for.- double_claim
Which clashes the double-claim gate refuses, one of
"co_presence"(default) or"measured". See Which double claims are refused. Only read atlevel >= 1L.
Value
A tibble with lon, lat, area_code (integer, the container's
reporting code), level_polity_code (character, NA at level 0),
level (integer), cell_area_frac, polycell_id, start_year,
end_year, cell_area_ha and land_area_ha. At level 0 with
grid_vintage = "snapshot_2015" the six columns .carbon_cell_support()
returns are passed through unchanged; with "year_aware" those six
arrive alongside start_year and end_year.
Which vintage of the support level 0 is read at
The support is a table of cell-to-polity claims with validity intervals, so "which polity holds this cell" has an answer per year. Two answers are selectable and neither is ever a fallback for the other:
"snapshot_2015"One reference year,
.carbon_support_year(), folded onto reportingarea_codes – the carbon path's DA-28 / whep#549 choice that migrating the territorial EXTENT does not migrate the ATTRIBUTION. Every year of a run is allocated into the SAME present-day geography."year_aware"The support in its own interval grain, keyed and folded per epoch, so
.filter_country_grid_year()selects the polities valid in each simulation year – the same treatment a granted depth gets. A cell held by a polity that has no reporting code in that year, or by no polity at all, is then absent for that year.
The two are different geographies, not two precisions of one, and the
difference is large before about 1990: on the 20260825T102349Z-1a0eb
support the year-aware read covers 41,597 cells and 7,942 Mha of land at
1851 against the snapshot's 66,709 cells and 12,926 Mha. Of that year's
LUH2 cropland, 41.6% is taken away from the country the snapshot gives it
to and 21.5% is given to a country the snapshot does not – the two
directions are reported separately, because a metric that scores only the
losses cannot see a share that absorbs one country's land into another.
101.8 Mha of the loss falls in cells no polity claims in 1851 and is
attributed to nobody. At 2015 the two vintages agree exactly, which is the
check that fails if the year-aware share is renormalised.
Those figures are the 20260825T102349Z-1a0eb support's, not the
20260827T190201Z-f82a2 one's: that later pin books every hectare of
inland water and ice as land (whep#1010), which moves the cell count and
the land, though not the argument.
run_spatialize() records the resolved value in run_metadata.yaml and in
a method_grid_vintage column on every output it writes.
"snapshot_2015" is still the default, against whep#1000 T31(j)'s stated
preference, because WHEP's national tables are on a constant-territory
basis and the support is on a historical-polity one. In 1961
country_areas reports the Russian Federation, Kazakhstan and Ukraine
where the year-aware grid offers only the USSR, so 17.2% of the world's
harvested area has no cell to land in and is dropped whole;
validation/spatialize_grid_vintage.R measures it per year. Reconciling
the two bases is a lineage step on the national side, not a grid setting.
Which rows count as a level
A polity is at depth d when the containment edge (polity_containment)
places it d steps inside a container that is not itself contained. An edge
is admitted only when its container resolves, through
polity_area_crosswalk, to a single reporting area_code and is not of
polity_type "aggregate". An aggregate's reporting code is a matrix
bucket: 999 alone pools 62 territories, so filing a province under one
would attribute it to Rest of World rather than to a country. Dropped edges
are counted and named, never skipped in silence.
How the share of the cell is measured
cell_area_frac is the unit's share of the physical cell on the land
basis – land_area_ha / cell_land_ha, the same basis
.carbon_attach_land_share() uses at level 0, with the cell's whole
measured land as the denominator. Everything the fraction later splits
(LUH2 class areas, crop-pattern hectares) is already land-only, so dividing
by the whole cell would subtract the water twice. Because the support is
read in its interval grain, the denominator is evaluated per interval-start
year: the cell's land partition genuinely differs between epochs, and taking
one denominator across all of them would count a cell once per epoch.
Two support shapes are recognised, and which one is in hand is detected, never assumed:
- replacement
The support carries the units instead of their container (the shape a level-tagged pin delivers).
land_area_hais already the unit's absolute land, so the share is taken directly. A container row surviving beside its own members in one cell is refused: both claim the same ground, and choosing one silently is exactly the double count this epic exists to remove.- nested
The support carries a within-container share column (
container_area_frac,container_fracorparent_frac) alongside the container's own row. The unit's share of the cell is then the compositionf_unit x f_container, withf_containermeasured from the container's own row on the same land basis.
Which double claims are refused
A support that carries a container's own row beside its units' rows in one
cell claims that ground twice: cell_land_ha is the sum over the cell, so
every unit's share is divided by a total counting the same ground twice,
and nothing downstream can see it because the container's national total
still reconciles. The two rules are alternatives, never a fallback.
"co_presence"The default, and fail-closed: a container row found beside its own units is refused, full stop. It is the strict rule because this table carries no geometry – whether the two polygons overlap cannot be read off it.
"measured"Refuses only a container the cell's own area REFUTES: at least one shared cell-epoch whose whole claimed territory exceeds
cell_area_ha. A cell cannot hold more territory than it has, so an excess is proof; but the proof is ONE-SIDED, which is why this is the weaker rule and not the default. A cell half of which is ocean can hold a duplicate under its own area and show no excess. Every clash it passes over is warned about by container and cell-epoch count.
On the 20260827T190201Z-f82a2 support the measurement separates the two
cleanly: over the ten containers the shipped edge table admits at depth 1,
3,285 of 3,965 clashing cell-epochs over-claim by 529.03 Mha in total, and
the whole of that sits in eight containers whose units are genuinely
nested – Alaska inside the USA alone over-claims 150 Mha over 1,156 of its
1,372 shared cells. The two that over-claim nothing are Ryukyu inside Japan
1895-1945 (8 cells, 0 Mha) and Singapore inside Malaysia (1 cell, 0 Mha),
whose ground in the shared cell is disjoint. The decision is taken per
CONTAINER rather than per cell for that reason: a nested container's
coastal cells hide their own duplicate, while its interior cells prove the
nesting outright.
Which containers a depth is read for
Without containers, every edge polity_containment admits at this depth
is read, whoever the run is for. On the shipped edge table that is ten
containers with support rows – Alaska inside the USA, three 1949
Indonesian units, Manchuria inside three Chinese epochs, Serbia inside
three Yugoslav ones, Singapore inside Malaysia – and each of them is
carried in the polycell support BESIDE its own units, so
.level_check_no_double_claim() refuses the read. The refusal is right:
continuing would divide every unit's share by a cell land total that
counts the same ground twice. It is simply not the Japanese run's
business.
containers scopes the edge set to the containers asked for, and that is
all it does. Scoping is not suppressing: the double-claim gate, the
aggregate-container refusal and the support-empty abort all still run, on
the scoped edges, so a granted container whose own row survives beside its
units aborts exactly as before. A container named here that no edge places
at this depth aborts with class whep_level_container_not_admitted rather
than returning a grid that silently lacks it.
Examples
# Offline: a two-cell support for one Japanese prefecture, with the
# containment edge that puts it inside Japan. No pin and no network.
support <- tibble::tribble(
~polycell_id, ~cell_id, ~lon, ~lat, ~polity_code,
"JPN-23-1871-2025@1", 1L, 137.25, 35.25, "JPN-23-1871-2025",
"JPN-23-1871-2025@2", 2L, 137.75, 35.25, "JPN-23-1871-2025"
) |>
dplyr::mutate(
area_code = 110L,
start_year = 1952L,
end_year = 2025L,
cell_area_ha = 3000,
land_area_ha = c(1200, 2000)
)
containment <- tibble::tibble(
member_code = "JPN-23-1871-2025",
container_code = "JPN-1952-2025",
start_year = 1952L,
end_year = 2025L,
basis = "prefecture inside JPN-1952-2025 for those years"
)
read_level_country_grid(
level = 1L,
support = support,
containment = containment
)
#> No `containers` scope: every admitted containment edge is read (1 container).
#> ℹ country_grid: polycell support at level 1, 2 compartments over 2 cells in 1 container.
#> # A tibble: 2 × 11
#> lon lat area_code level_polity_code level cell_area_frac polycell_id
#> <dbl> <dbl> <int> <chr> <int> <dbl> <chr>
#> 1 137. 35.2 110 JPN-23-1871-2025 1 1 JPN-23-1871-2025…
#> 2 138. 35.2 110 JPN-23-1871-2025 1 1 JPN-23-1871-2025…
#> # ℹ 4 more variables: start_year <int>, end_year <int>, cell_area_ha <dbl>,
#> # land_area_ha <dbl>
# The same read scoped to the containers a run grants a depth. Japan's
# reporting code is 110, so the edge above is kept and any other
# container's is left at level 0.
read_level_country_grid(
level = 1L,
support = support,
containment = containment,
containers = 110L
)
#> ℹ country_grid: polycell support at level 1, 2 compartments over 2 cells in 1 container.
#> # A tibble: 2 × 11
#> lon lat area_code level_polity_code level cell_area_frac polycell_id
#> <dbl> <dbl> <int> <chr> <int> <dbl> <chr>
#> 1 137. 35.2 110 JPN-23-1871-2025 1 1 JPN-23-1871-2025…
#> 2 138. 35.2 110 JPN-23-1871-2025 1 1 JPN-23-1871-2025…
#> # ℹ 4 more variables: start_year <int>, end_year <int>, cell_area_ha <dbl>,
#> # land_area_ha <dbl>
# The same support read at level 0. The default vintage is year-aware,
# which takes this support and keeps its validity intervals; the 2015
# snapshot resolves the support itself and refuses one.
read_level_country_grid(
level = 0L,
support = support,
grid_vintage = "year_aware"
)
#> ℹ country_grid: polycell support, year-aware, 2 compartments over 2 cells in 1 epoch.
#> # A tibble: 2 × 8
#> lon lat area_code cell_area_ha land_area_ha cell_area_frac start_year
#> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <int>
#> 1 137. 35.2 110 3000 1200 1 1952
#> 2 138. 35.2 110 3000 2000 1 1952
#> # ℹ 1 more variable: end_year <int>