Disaggregate country-level FAOSTAT crop harvested areas to a 0.5-degree grid. This reproduces the core spatialization workflow of the LandInG toolbox, adapted to WHEP conventions and tidy data structures.
The algorithm follows three main steps:
Each crop's country total is distributed to grid cells proportionally to a spatial reference pattern (e.g. Monfreda) weighted by gridded cropland extent (e.g. LUH2/HYDE).
If total allocated harvested area in any cell exceeds its capacity (cropland times multi-cropping suitability), excess is iteratively redistributed using a logit-based transformation.
Individual crops are aggregated into crop functional types (CFTs).
Usage
build_gridded_landuse(
country_areas,
crop_patterns,
gridded_cropland,
country_grid,
config = list()
)Arguments
- country_areas
A tibble with country-level crop harvested areas. Expected columns:
year: Integer year.area_code: Country code (numeric, matching WHEP polities).item_prod_code: FAOSTAT item code for the crop.harvested_area_ha: Total harvested area in hectares.irrigated_area_ha: Irrigated harvested area in hectares (optional, defaults to 0).
- crop_patterns
A tibble with per-cell spatial crop patterns. Expected columns:
lon: Longitude of cell centre.lat: Latitude of cell centre.item_prod_code: FAOSTAT item code.harvest_fraction: Cropping intensity (Monfreda area divided by reference cropland).
- gridded_cropland
A tibble with per-cell cropland extent. Expected columns:
lon: Longitude of cell centre.lat: Latitude of cell centre.year: Integer year.cropland_ha: Total cropland area in hectares.irrigated_ha: Irrigated cropland in hectares (optional, defaults to 0).
- country_grid
A tibble mapping grid cells to countries. Expected columns:
lon: Longitude of cell centre.lat: Latitude of cell centre.area_code: Country code.cell_area_frac(orpolity_frac,area_frac,country_frac): This polity compartment's share of the physical cell, a partition summing to 1 over the polities that overlap the cell. Required: a grid carrying no share is refused, because defaulting it to 1 gives a border cell wholly to one polity. Pass 1 only where the polity does own the whole cell. A land fraction (landfrac) is a different quantity and is refused rather than reinterpreted. Optional columns:polycell_id,cell_id: Stable compartment/cell identifiers preserved in outputs when present.yearor validity intervals (valid_from/valid_to,start_year/end_year,from_year/to_year) for historical, time-varying polity overlays. The start bound is inclusive; the end bound is exclusive at a succession and inclusive at the open end, so 2014 selects"RUS-2014-2025"and not"RUS-1991-2014", while 2025 still selects"RUS-2014-2025"because no later interval of that compartment follows it. See polities for the full rule.
- config
Named list of optional extras. Unknown keys raise an error. Recognised keys:
years: Integer vector of years to spatialize. IfNULL(default), all years present incountry_areasare processed. When supplied,country_areas,gridded_cropland, andtype_croplandare filtered to this set before processing.cft_mapping: A tibble mapping FAOSTAT items to CFT names (item_prod_code,cft_name). IfNULL, no CFT aggregation is performed and individual crop results are returned.type_cropland: A tibble with per-cell, per-year, per-type cropland (lon,lat,year,luh2_type,type_ha,type_irrig_ha). When provided alongsidetype_mapping, each crop is allocated only into cells containing its LUH2 type. IfNULL, falls back to total cropland.type_mapping: A tibble (item_prod_code,luh2_type) that maps each crop to its LUH2 type. IfNULL, type-aware allocation is disabled even whentype_croplandis provided.multicropping: A tibble with per-cell multi-cropping suitability factors. Required columns:lon,lat,mc_rainfed,mc_irrigated. An optionalyearcolumn keys factors to year (one row per cell per year); when present, the table is filtered to the current year before the capacity constraint is applied. When absent, the table is treated as a static spatial layer applied to every year. IfNULL(default), the capacity constraint still runs withmc_rainfed = mc_irrigated = 1(harvested area capped at physical cropland).max_iterations: Maximum iterations for the redistribution loop. Default:1000L.polity_support: the unfolded polity support, used to reconcilecountry_areas' polity vintage with a year-awarecountry_grid's. Absent, the reconciliation is skipped, which is correct for a snapshot grid. Seeread_polycell_support().pattern_signal_floor: Theharvest_fractionbelow which acrop_patternscell is treated as float underflow rather than an allocated area, and zeroed before the placement weights are formed. Default1e-12, argued from EarthStat's own float32 precision in.crop_pattern_signal_floor().0restores the untoleranced behaviour of whep#1070, in which a (country, crop) whose whole pattern is underflow is placed proportional to that underflow instead of uniformly.expansion_threshold: defunct. It was accepted but never changed the allocation, and is dropped with a warning (whep#1001). LandInG's expansion step is not implemented.area_key: Which area code the output is keyed on,"grid"(default) or"polity_area". See Which area code the output is keyed on.mc_factor: Which multi-cropping factor sets the capacity ceiling of a granted-depth compartment,"unit"(default) or"national". See The capacity ceiling and its breach. A grid with nolevel_polity_codehas no unit, so the two are the same table there and a level-0 run is unaffected.pattern_extension:"none"(default) or"granted_units". Under"granted_units", a granted-depth compartment carrying nocrop_patternsrow for an item the national table gives it a target for gains one atharvest_fraction = 0, so the crop is placed uniformly over that unit's cropland of its LUH2 type instead of being dropped. Level-0 rows are never extended.
Value
A tibble with gridded crop (or CFT) harvested areas. Columns:
lon,lat: Cell centre coordinates.year: Integer year.area_code: WHEP polity code for this cell compartment.polity_area_code,reporting_polity_code,reporting_polity_name,reporting_polity_has_geometry: Polity metadata forarea_code.grid_area_code: Only underarea_key = "polity_area"; the reporting code the engine allocated on.polycell_id,cell_id: Preserved when supplied incountry_grid.crop_nameorcft_name: Crop or CFT identifier.rainfed_ha: Rainfed harvested area in the cell.irrigated_ha: Irrigated harvested area in the cell.
The per-compartment capacity breach is a second table and is
therefore not returned here: this function's return value is a
contract two in-package consumers and every run_spatialize() caller
already depend on, and an attribute would ride through every dplyr
verb into comparisons that are about the allocation. Allocate at a
granted depth with allocate_level_crops(), which returns the breach
beside the allocation. See The capacity ceiling and its breach.
The capacity ceiling and its breach
The ceiling is soft: .redistribute_country_dt() rescales every crop
back to its target after the logit passes, so where a compartment's
cells cannot hold the target the target wins and the ceiling gives
way. That excess is measured rather than absorbed, and returned as the
breach table of allocate_level_crops(): one row per compartment,
cell, item and mc_basis, with rf_over and ir_over in hectares,
which T14's reconciliation consumes as a table and never as warning
text. A cell's ceiling
is shared by every item in it, so the cell's excess is attributed to
items pro rata by their allocated area – no other split is
available, and summing the rows of a cell recovers its excess exactly.
mc_basis records which multi-cropping factor the row was scored
against. Rows carrying a level_polity_code are scored twice: at
"national", the factor the multicropping layer supplies (which is
a national figure broadcast to every cell, so it inflates a unit's
breach by construction), and at "unit", the unit's own implied
intensity – its whole allocated harvested area over the same physical
cropland the ceiling uses, floored at 1 because a factor below 1 would
forbid single cropping on land that exists. in_force marks the basis
mc_factor selected, which is the one the redistribution ran against.
The unit factor makes a unit's aggregate ceiling equal its own
allocation by construction, so a breach at "unit" is always a
within-unit concentration, never a shortfall of the unit as a whole;
that is the difference the pair of bases exists to show. The plan
phrases the unit factor as unit harvested over unit herbaceous LUH2
cropland; the ceiling carries no LUH2 type split, so it is computed
over the same cropland the ceiling itself multiplies, and a
herbaceous-only denominator would leave the two inconsistent.
Which area code the output is keyed on
The chain allocates from a national table keyed on area_code and
into a country_grid keyed the same way, so both sides speak the raw
reporting vocabulary the grid was rasterized in. WHEP's polity-keyed
national tables are aggregated on polity_area_code instead, a bucket
that a reporting code need not equal: 276 Sudan and 277 South Sudan
both fall in bucket 206. Every such output row therefore carries two
territorial keys that disagree, and whether a consumer joins on
area_code or on polity_area_code decides whether Sudan exists in its
result (whep#582).
area_key selects which of the two the output carries. It is not a
fallback: "grid" is the default, reproduces today's codes
bit-for-bit, and warns naming the codes that cannot join;
"polity_area" resolves each code to its bucket through
polity_area_crosswalk before the polity columns are attached, so
area_code and polity_area_code agree in every row. It respects
options(whep.unfold_rest_of_world) (see folded_reporting_areas()),
so the output and the national tables agree about where a Rest-of-World
member's rows belong.
Under "polity_area" the raw reporting code is carried, not
replaced: the output gains grid_area_code, joined with + where two
reporting areas of one bucket meet in a cell and their rows collapse. So
the fold stays recoverable at the join rather than baked into the output,
the shape build_cell_polity() adopted for the same reason (whep#579).
Methodology
This function reimplements the spatial crop allocation from the LandInG toolbox (Ostberg et al. 2023, doi:10.5194/gmd-16-3375-2023) with the following extensions:
LUH2 crop-functional-type constraints (
type_cropland+type_mappingparameters) restrict each crop to cells containing its LUH2 type (c3ann, c4ann, c3per, c3nfx). LandInG allocates to total cropland without type constraints.MIRCA2000 crop-specific irrigated fractions (Portmann et al. 2010) for irrigation distribution, falling back to LUH2-proportional allocation.
Data sources
Country areas: FAOSTAT QCL via
build_primary_productionCrop patterns: EarthStat / Monfreda et al. (2008)
Gridded cropland: LUH2 v2h (Hurtt et al. 2020)
Irrigation: MIRCA2000 (Portmann et al. 2010) + LUH2
Examples
# Minimal example with toy data
country_areas <- tibble::tribble(
~year, ~area_code, ~item_prod_code, ~harvested_area_ha,
2000L, 1L, 15L, 1000
)
crop_patterns <- tibble::tribble(
~lon, ~lat, ~item_prod_code, ~harvest_fraction,
0.25, 50.25, 15L, 0.6,
0.75, 50.25, 15L, 0.4
)
gridded_cropland <- tibble::tribble(
~lon, ~lat, ~year, ~cropland_ha,
0.25, 50.25, 2000L, 800,
0.75, 50.25, 2000L, 500
)
country_grid <- tibble::tribble(
~lon, ~lat, ~area_code, ~cell_area_frac,
0.25, 50.25, 1L, 1,
0.75, 50.25, 1L, 1
)
build_gridded_landuse(
country_areas, crop_patterns, gridded_cropland, country_grid,
config = list(years = 2000L)
)
#> → Year 2000: 2 rows (alloc 0.02s, cap 0.03s)
#> # A tibble: 2 × 11
#> year area_code polity_area_code reporting_polity_code reporting_polity_name
#> <int> <int> <int> <chr> <chr>
#> 1 2000 1 1 ARM-1991-2025 Armenia
#> 2 2000 1 1 ARM-1991-2025 Armenia
#> # ℹ 6 more variables: reporting_polity_has_geometry <lgl>, lon <dbl>,
#> # lat <dbl>, item_prod_code <int>, rainfed_ha <dbl>, irrigated_ha <dbl>
