SEEA EA 2024 · Annex worked example · Phase 1 replication target
SEEALand, end to end
The standard's own toy landscape: 250 hectares, six ecosystem types, one accounting
year, and a single 2-hectare forest→cropland conversion that propagates through all five accounts.
Reproducing these exact numbers is the cheapest possible proof that our engine implements the
standard and not an approximation of it.
Every number on this page is synthetic. SEEALand is a stylised example invented by the
standard's authors — there is no study site, no satellite imagery, no survey. The hectare grid, the
condition variables, the service flows and the prices are all assumed values chosen to make the
arithmetic legible.
What is real is the vocabulary and the algebra: the IUCN GET ecosystem types, the
Ecosystem Condition Typology, the ES reference list, the supply-and-use structure, and the NPV
decomposition. That is precisely what makes it a good fixture — it pins down our account arithmetic
with no data dependency at all. Where the diagram below names a real catalog layer, it is marked
unseea and describes what we will substitute later, not
something SEEALand uses.
- Accounting area
- 250 ha
- Ecosystem types
- 6
- Period
- 2020
- The one change
- 2 ha
- GEP
- $83,125
- Asset value Δ
- +$51,244
The landscape, and the one thing that happens to it
Each square is 1 ha, drawn from the standard's own spreadsheet
(figures AI.1–AI.2, p. 371). The whole extent account traces back to these two
grids, and the entire difference between them is two cells at the bottom-left of the forest block.
Closing extent31 Dec 2020
The conversion (outlined in rust): 2 ha of forest becomes cropland. In the accounts this
is one managed reduction and one managed expansion — but for forest it moves extent,
condition, service flows, GEP and asset value through four different mechanisms. That propagation
is what the replication actually tests.
Six storylines, not one
A common misreading — and one an earlier draft of this page invited — is that the
2-hectare conversion drives every number in the accounts. It does not. The annex
(p. 370) sets out a changing ecological context in which several
independent pressures act at once. The conversion drives the forest story; seagrass's
$163,946 of degradation has nothing to do with it.
Forest
Converted at the margin, and the remaining block suffers edge effects that
impair its ecological functioning. This is the one storyline the conversion causes.
condition −0.0590
Lake
Long-term efforts to improve water quality pay off. The only ecosystem type that
improves, and the only one posting ecosystem enhancement.
condition +0.0456
Cropland
Gains 2 ha from the conversion. Policies to improve cropland condition have
mixed outcomes, so its condition is nearly flat.
condition +0.0034
Urban area
Urban intensification drives a loss of green space within an unchanged
footprint — extent is static while condition falls.
condition −0.0192
Wetland
Caught by the same general intensification of ecosystem use as forest,
without any change in its own extent.
condition −0.0353
Seagrass
Sewerage overflow from the urban area. The worst condition decline in
SEEALand, and entirely unrelated to the land conversion.
condition −0.0759
Prices move too, and for stated reasons. Wood and wild fish prices rise on higher demand
plus tighter sustainability regulation constraining supply; the price of global climate regulation
rises to reflect increasing marginal damages from carbon release. These price changes are what
produce the revaluation entries in the asset account — and, as the closing section shows, they are
large enough to flip the sign of the headline number.
The five accounts, in dependency order
The numbering is real: each account consumes the one above it. Extent defines the rows
every later table is indexed by; condition and service flows are measured per ecosystem type;
monetary values price the flows; the asset account discounts them forward and reconciles the opening
and closing balance sheet.
What the step does
Partitions the accounting area into mutually exclusive, exhaustive
ecosystem types and measures each one's area at the opening and closing dates.
Change is not just a delta — it is split into managed vs. unmanaged
expansion and reduction, and cross-tabulated as a from→to change matrix
whose diagonal is unchanged area.
Inputs
SEEALand — assumed
- A hand-drawn 1-ha cell map at two dates, typed to IUCN GET Level 3 (EFG)
unseea — real layers
- cgls-lc100-2019/hex-fractions res-9 areal fraction per class in catalog
- overturemaps/countries/hex res-8, as the accounting area in catalog
- A second date for real change accounting — annual NLCD, then ESA CCI LC pending
What comes out
- Area per ET: opening / additions / reductions / closing
- The ET change matrix — the only place conversions are recorded
- The row index every downstream account uses
SEEALand values to reproduce
Forest 40 → 38 ha
Cropland 60 → 62 ha
Lake / Urban / Wetland / Seagrass unchanged
Total 250 ha, net change 0
SQL — the real-data equivalent4.3 s · validated
WITH aoi AS ( -- the ecosystem accounting area, as res-8 hexes
SELECT DISTINCT h8
FROM read_parquet('s3://public-overturemaps/2026-02-18.0/countries/hex/h0=*/data_0.parquet')
WHERE h0 IN (578290339652042751, 578395892768309247)
AND country = 'CR' AND class = 'land'
)
SELECT f.lc_class,
ROUND(SUM(f.frac * h3_cell_area(f.h9, 'km^2')), 1) AS area_km2
FROM read_parquet('s3://public-land-cover/cgls-lc100-2019/hex-fractions/h0=*/data_0.parquet') f
SEMI JOIN aoi a ON f.h8 = a.h8
WHERE f.h0 IN (578290339652042751, 578395892768309247)
AND f.lc_class NOT IN (0, 200, 80, 255) -- no-data, open sea, water, sentinel
GROUP BY f.lc_class
ORDER BY area_km2 DESC;
frac × cell area is the primitive the whole account rests on.
Over Costa Rica it returns 51,078 km² against the country's own land polygon at 51,181 km² —
−0.20%. Constraining h0 is partition pruning for speed; the
SEMI JOIN is what actually clips to the accounting area.
What the step does
2a Variables. Observe raw measurements per ET, filed under the SEEA
Ecosystem Condition Typology: A1 physical, A2 chemical, B1 compositional, B2 structural,
B3 functional, C1 landscape.
2b Indicators. Rescale each variable to [0,1] against a lower and upper
reference level. Direction can invert — for lake nitrogen the lower bound is
2 mg/l and the upper is 0.
2c Index. Weight indicators up the ECT tree into one index per ET. Forest
weights: abiotic ⅓, biotic ½, landscape ⅙.
Inputs
SEEALand — assumed
- Forest: NDWI, soil organic carbon, foliar N, tree species richness, tree cover, NDVI, forest area density
- Reference levels stated outright — natural for forest/lake/wetland/seagrass, anthropogenic for cropland/urban
unseea — real layers
- B1 globio-msa-2015-overall — already [0,1] vs. undisturbed, so 2b is the identity in catalog
- C1 h3_grid_disk over hex land cover — the forest-area-density analogue in catalog
- A2 SoilGrids SOC · A1 HydroSHEDS, Global Surface Water pending
- Reference-level tables per ET and biome — the hardest unsolved piece pending
What comes out
- A condition index per ET, opening and closing
- Net change decomposed into Δabiotic / Δbiotic / Δlandscape
- The volume effect input to stage 5 — condition change is what degradation and enhancement mean in money
SEEALand values to reproduce
Forest 0.6707 → 0.6118
Δ forest −0.0590
of which landscape −0.0250
Lake +0.0456
Seagrass −0.0759
Cropland +0.0034
SQL — B1 compositional condition, by ecosystem type6.5 s · validated
WITH aoi AS (
SELECT DISTINCT h8
FROM read_parquet('s3://public-overturemaps/2026-02-18.0/countries/hex/h0=*/data_0.parquet')
WHERE h0 IN (578290339652042751, 578395892768309247)
AND country = 'CR' AND class = 'land'
),
lc AS ( -- how much of each ET sits inside each res-8 cell
SELECT h8, lc_class, SUM(frac * h3_cell_area(h9, 'km^2')) AS et_km2
FROM read_parquet('s3://public-land-cover/cgls-lc100-2019/hex-fractions/h0=*/data_0.parquet')
WHERE h0 IN (578290339652042751, 578395892768309247)
AND lc_class NOT IN (0, 200, 80, 255)
GROUP BY h8, lc_class
)
SELECT lc.lc_class,
ROUND(SUM(g.msa * lc.et_km2) / SUM(lc.et_km2), 4) AS msa_by_et,
ROUND(SUM(lc.et_km2), 1) AS et_area_km2
FROM lc
JOIN read_parquet('s3://public-globio/globio-msa-2015-overall/hex/h0=*/data_0.parquet') g
ON g.h8 = lc.h8 AND g.h0 IN (578290339652042751, 578395892768309247)
SEMI JOIN aoi a ON lc.h8 = a.h8
GROUP BY lc.lc_class
ORDER BY et_area_km2 DESC;
MSA is a bounded intensity, so this is an area-weighted mean, never
a sum. The result orders as ecology predicts — closed broadleaf forest 0.544,
cropland 0.266, urban 0.241 — which is the real evidence the join is right. MSA needs no
rescaling step: it already runs [0,1] against an undisturbed reference, so stage 2b is the
identity for this one variable.
SQL — C1 landscape: forest area density over a k-ring3.4 s · validated
WITH aoi AS (
SELECT DISTINCT h8
FROM read_parquet('s3://public-overturemaps/2026-02-18.0/countries/hex/h0=*/data_0.parquet')
WHERE h0 IN (578290339652042751, 578395892768309247)
AND country = 'CR' AND class = 'land'
),
forest AS ( -- neighbour universe: NOT clipped, so k-rings aren't truncated
SELECT h9, h8, SUM(frac) AS forest_frac
FROM read_parquet('s3://public-land-cover/cgls-lc100-2019/hex-fractions/h0=*/data_0.parquet')
WHERE h0 IN (578290339652042751, 578395892768309247)
AND lc_class BETWEEN 111 AND 126
GROUP BY h9, h8
),
focal AS ( -- but score only the cells inside the accounting area
SELECT f.h9 FROM forest f SEMI JOIN aoi a ON f.h8 = a.h8
),
nbr AS (
SELECT f.h9 AS focal, UNNEST(h3_grid_disk(f.h9, 3)) AS neighbour FROM focal f
)
SELECT n.focal,
AVG(COALESCE(f2.forest_frac, 0)) AS forest_area_density
FROM nbr n LEFT JOIN forest f2 ON f2.h9 = n.neighbour
GROUP BY n.focal;
This is the variable that carries SEEALand's forest decline —
74% → 59% in the annex. On an H3 grid the metric that normally needs dedicated raster tooling
is one UNNEST. Measured: 404,240 focal cells at k=3 in 3.4 s; raising k from 1 to
40 multiplies rows by 700× but wall clock by only 9×, so the neighbourhood radius is
effectively free and EAA size is what binds.
What the step does
For each service, quantifies the flow generated in the period and records it in a
supply-and-use table: supplied by ecosystem type, used by industry,
government or households.
Total supply must equal total use, service by service. Services used by other
ecosystems are intermediate and must not be counted in the aggregate.
Inputs
SEEALand — assumed
- Stated physical flows for 6 services across the 6 ETs, with no model behind them
unseea — real layers
- Per-service logic chains from the UNSD biophysical modelling guidelines pending
- Climate regulation: carbon stock and flux layers pending
- Extent (stage 1) and condition (stage 2) both enter as modifiers
What comes out
- Physical supply-and-use table, balanced
- Expected opening and closing flows per ET — the quantity series stage 5 discounts
SEEALand values to reproduce
Wood 140 m³
Crop 150 t
Wild fish 9 t
Climate regulation 425 tCO₂
Water purification 7 t N
Recreation 9,800 visits
SQL — the one service we can compute today: carbon retention3.8 s · validated
WITH aoi AS (
SELECT DISTINCT h8
FROM read_parquet('s3://public-overturemaps/2026-02-18.0/countries/hex/h0=*/data_0.parquet')
WHERE h0 IN (578290339652042751, 578395892768309247)
AND country = 'CR' AND class = 'land'
)
SELECT ROUND(SUM(c.carbon) / 1e6, 2) AS mt_irrecoverable_c
FROM read_parquet('s3://public-carbon/irrecoverable-carbon-2024/hex/h0=*/data_0.parquet') c
SEMI JOIN aoi a ON c.h8 = a.h8
WHERE c.h0 IN (578290339652042751, 578395892768309247);
SUM is correct here, and it is the exact opposite of the MSA rule
above. carbon is a per-cell total (an amount), so it sums; MSA is a
per-cell intensity, so it averages. The two look identical in SQL and produce silently wrong
accounts if swapped. Costa Rica: 52.93 Mt C irrecoverable, 2024.
To split it by ecosystem type you apportion the amount by
frac and then sum — the mirror image of weighting an intensity by area
and then averaging. Both layers are native res 9, so that join is exact. The rest of the
service list needs the biophysical logic chains and is not yet computable.
What the step does
Prices each physical flow at its exchange value — the price it would fetch
in a market, deliberately not a welfare or willingness-to-pay value.
Summing final services supplied gives Gross Ecosystem Product, the
headline aggregate that sits alongside GDP.
Inputs
SEEALand — assumed
- Physical flows from stage 3
- Prices: wood $60/m³, crop $75/t, fish $350/t, CO₂ $25/t, N $100/t, visit $5
unseea — real layers
- Prices become a visible control panel defaulting to SEEALand's, because the carbon price is genuinely contested pending
What comes out
- Monetary supply-and-use table
- GEP, and its split by ecosystem type
- The price series stage 5 needs for revaluation
SEEALand values to reproduce
Forest $19,650
Lake $26,050
Cropland $11,250
Urban $12,625
Wetland $1,200
Seagrass $12,350
GEP $83,125
No SQL — and that is the pointarithmetic, not a query
Stages 4 and 5 are price × quantity and a discount factor.
There is no spatial data to query: once stage 3 has produced physical flows per ecosystem
type, the monetary accounts are ordinary arithmetic over a small table.
That is exactly why Phase 1 is blocked on nothing. We can
implement and test the entire monetary half of the standard against SEEALand's own numbers
without a single catalog layer — and the prices, discount rate and asset life become visible
controls in the app rather than buried constants, because the standard itself treats them as
illustrative rather than prescribed.
What the step does
Discounts each ET's expected future service flows to a net present value —
100-year asset life, 2% real discount rate, income at period end — then reconciles opening to
closing value.
The reconciliation is the clever part. The NPV change is decomposed into an
area, a volume and a price effect, and each
maps onto a different accounting entry.
Inputs
SEEALand — assumed
- Expected flows and prices, opening and closing (stages 3–4)
- Asset life 100 years, discount rate 2% real — stated as illustrative, not prescribed
unseea — real layers
- Area per ET (stage 1) drives the area effect; condition change (stage 2) drives the volume effect
- Discount rate and asset life become user-visible controls, SEEALand's values as defaults pending
What comes out
- NPV per ET, opening and closing
- The full balance-sheet reconciliation: enhancement, degradation, conversions, reappraisals, revaluation
- Area effect → conversion; volume effect → degradation / enhancement; price effect → revaluation
SEEALand values to reproduce
Opening $3,572,207
Closing $3,623,451
Forest ΔNPV −$116,366
= area −$43,435
+ volume −$108,111
+ price +$35,180
The decomposition, as arithmeticSEEA EA appendix A10.1
NPV factorises exactly into three terms, per ecosystem type and service:
Q = expected annual flow × asset life a = area, hectares
q = Q / a quantity per hectare p = NPV / Q, implied unit price
NPV = a · q · p
The change is split symmetrically across the three factors — the Shapley
decomposition of a product — so the parts sum exactly to the whole, with
no residual left over to dump somewhere:
area = (a₁−a₀) · ( (q₀p₀ + q₁p₁)/3 + (q₀p₁ + q₁p₀)/6 ) → CONVERSION
volume = (q₁−q₀) · ( (a₀p₀ + a₁p₁)/3 + (a₀p₁ + a₁p₀)/6 ) → DEGRADATION / ENHANCEMENT
price = (p₁−p₀) · ( (a₀q₀ + a₁q₁)/3 + (a₀q₁ + a₁q₀)/6 ) → REVALUATION
Forest, all three services summed:
−43,435 + −108,111 + +35,180 = −116,366
This decomposition is the load-bearing idea of the whole standard.
Without it you have a single number for the change in asset value and no way to post it: a
conversion, a degradation and a price rise are three different accounting entries that happen
to sum to the same total, and only one of them is something a policy did.
The symmetric form matters — a naive
p0·q0·(a1−a0) style split leaves a residual that has to be dumped somewhere. These
formulae reproduce the annex's forest figures to machine precision, verified against
npv-decomposition.csv.
Following the 2 hectares all the way through
One conversion, five accounts, four distinct mechanisms. This single row of the fixture
is the most useful regression test in the whole example — if our engine gets forest right, the
plumbing is right.
Where the balance sheet moves
The closing asset account, by entry type. Note that the total rises even though three
of six ecosystem types degrade — revaluation and upward reappraisal more than offset the physical
loss. Being able to show that split is much of the point of the standard.
Four ways to get this wrong
Each of these is something the standard warns about explicitly, and each is something a
naive SQL implementation does by default.
Averaging condition across ecosystem types
Forest and cropland are scored against different reference levels — one natural, one
anthropogenic. A mean over both is meaningless (§5.4.1). Condition is reported per ET, full stop.
Summing an intensity, averaging an amount
MSA is a bounded [0,1] index and needs an area-weighted mean. Carbon stock is a
per-cell total and needs a sum. The two rules look identical in SQL and produce
silently wrong accounts.
Counting intermediate services in the aggregate
A service one ecosystem supplies to another is not part of GEP (§6.2.3). SEEALand has none,
which is exactly why it is a poor test of this and real accounts are not.
Treating the NPV change as one number
Without the area / volume / price decomposition you cannot post the entries — conversion,
degradation and revaluation are three different lines that happen to sum to the same total.
So what is the assessment actually telling us?
Two hectares of forest became cropland. Here is what the five accounts, read together,
say about that — and none of it is visible from any single account on its own.
1. The damage is wildly disproportionate to the area lost
You removed 5% of the forest — 2 hectares of 40. Look at what follows from it:
The conversion cut into the block rather than shaving its edge, creating disproportionate
perimeter. Forest area density is the single largest contributing variable to the condition
decline — −0.025 of the −0.059 total. Nothing else in the account would have caught
this, which is precisely why the C1 landscape class earns its place.
2. "Net zero hectares" hides a real loss
The extent account records net change in extent: zero. Two hectares left
forest, two hectares joined cropland. But forest is worth $22,627/ha and cropland
$8,081/ha, so the swap moves land from a high-value asset class to a low-value one:
And that is before the −$108,111 of forest degradation the edge effect helped
cause. An area-only account would have called this a wash.
3. The headline number went up while nature got worse
Total asset value rose +$51,244. Split by what actually happened:
Natural capital physically fell by $284,347. The balance sheet rose only because carbon
and timber became more valuable. Anyone reporting a single "natural capital value" would
have announced good news. This is the strongest argument for the standard's structure: it makes it
structurally impossible to book a price rise as an environmental improvement.
How this informs scenarios and policy
The reason to compute accounts rather than indicators is that accounts tell you which
lever moved — and therefore which lever to pull.
Siting beats quantity
Because the damage tracks configuration rather than area, the same 2 ha taken from a forest edge
versus punched into the interior give materially different outcomes. A scenario engine can rank
alternative sitings of an identical development — the actionable lever, and one that falls straight
out of the k-ring metric.
Flow and stock fail differently
GEP is an annual flow; the asset account is a stock. You can raise this year's GEP by harvesting
more timber while depleting the asset that produces it. Only holding both catches that, which is
why the standard refuses to collapse them into one indicator.
Two bad arguments get blocked
"We lost 2 ha of forest but gained 2 ha of cropland, so it's a wash" — no: −$26,491 before
induced degradation. And "natural capital is up this year" — no: that was carbon prices.
Where the credibility actually sits
The money moves through the volume effect, and the volume effect is condition. So all of
this rests on the account with no agreed reference levels and the thinnest data. That is the honest
weak point, and it is where the work should go.
The decomposition names the instrument. Each effect corresponds to a different kind of
policy, which is why posting them separately is not bookkeeping pedantry:
Why this doubles as the scenario engine's test
SEEALand is a scenario: an edit to extent, propagated through every account. Our
scenario mechanism does the same thing on real data — the user repaints hexes, stage 1 reruns, and
stages 2–5 recompute downstream. Getting the fixture to reproduce exactly means the propagation path
is correct before any real layer is trusted, and it stays as the regression test afterwards.
This is why Phase 1 is not blocked on anything. Replicating SEEALand needs no ingest, no
catalog layer and no second land-cover date. The data work and the correctness work run in
parallel by design.
Read the original
SEEALand is Annex I of the standard —
"SEEALand – a stylized example of ecosystem accounting", pp. 369–383
of System of Environmental-Economic Accounting —
Ecosystem Accounting (United Nations, 2024 official edition, ST/ESA/STAT/SER.F/124). The
complementary spreadsheet with the per-variable workings is published alongside it on the same page.