Overview

Plotline overview

Plotline is a compact store for EUDR forest-loss screening. It keeps only the facts the decision reads and gives the same answers as the original map layers.

In brief
  • The problem: EUDR screening intersects farm plots with years of forest maps, and repeats the check whenever a map updates. The challenge brief asks for compact storage, fast queries and the same plot-level answer as the original layers.
  • The result: a 6.1 KB decision layer replaces 623 KB of original layers, with identical answers for all 744,732 map cells.
  • The speed: registered plots re-check 44× faster and new boundaries about 5× faster. Every answer carries a receipt.
1 · The problem

EUDR plot-level screening

Companies that sell rubber, palm oil, cocoa or coffee into the EU must show that every supplying farm plot is deforestation-free under the EU Deforestation Regulation, EUDR.

A plot's boundary is checked against satellite maps for two things: whether the land was forest on 31 December 2020, the rule's cut-off date, and whether tree cover has been lost since. The maps come from different sources at different sharpness and update every year. The check is therefore re-run for millions of plots whenever a layer updates, and the data accumulates.

The brief's slide title frames the task: "Keep the decision, not the data." The layers are to be stored compactly and queried fast while reproducing exactly the same plot-level decisions as the original layers.

SchematicThe 31 December 2020 cut-off

Cut-offHansen records one loss year per map cell, 2001 to 2025.

A schematic of years, not data: no heights are measured values.

2 · The data

17 supplier plots and two public maps

A raster is a map stored as a grid of squares, one number per square, like graph paper. Satellite forest maps are rasters. A farm plot is a vector: an outline traced with coordinates. The task is laying outlines over grids.

Supplied sample

17 supplier plots

One shipment lot, one supplier. A CSV of 17 declarations (35 columns) and a GeoJSON of 17 matching outlines, in Chai Buri, Surat Thani, Thailand. Smallholdings of 0.53 to 20.07 ha. The two files join one to one.

Public · Hansen GFC 2025 v1.13

Tree-cover loss

Three bands at about 28 m: canopy in 2000, the year of loss (2001 to 2025), and a land/water mask. The chip is 1,233 × 604 = 744,732 cells.

Public · JRC GFC2020 v4

Forest in 2020

Forest or not, at the cut-off year, at about 9 m on a different grid (3,695 × 1,809). Each Hansen cell holds an exact 3 × 3 block of JRC cells.

PrivateThe pages show no real plot outline and no row from the supplier file. Every plot drawn is a labelled synthetic shape; only plot IDs and aggregate counts are quoted.
SyntheticOne Hansen cell holds nine JRC cells

Select a pattern for the nine small (JRC) cells to show what the written rule reads.

The rule reads

3 · Design

Plotline design: three questions, one decision layer

Plotline starts from the three questions an EUDR-style screen asks of a plot, and what each needs from the maps.

Q1

Was it forest on 31 Dec 2020?

Needs JRC's 2020 forest class, and land / water / unknown.

Q2

Has that forest been lost since?

Needs Q1 plus the Hansen loss year, 2021 to 2025.

Q3

What did the supplier's own check say?

The supplier named "Hansen Tree Cover Loss 2021-2023": any Hansen loss in 2021 to 2023, on any land.

No question reads the canopy percentages, which are 72% of Hansen's bytes, or loss years before 2021. Both are dropped. Each map cell is reduced to one of 20 codes.

Original layers reduced to a decision layer
Original layers623,237 B
canopy % 414,142 Bloss year 159,863 Bland/water mask 2,994 BJRC 2020 forest 46,238 B
Screening layer38,513 B · 93.8% smaller
Decision layer6,146 B · 99.0% smaller

SizeFour original files, 623,237 bytes.

Bars are to true scale on one sample region (34 × 17 km): bytes, not hectares or plots.

The screening layer answers all three questions. The decision layer answers Q1 and Q2 only, so it also forgets the loss year on land that was not forest in 2020: loss on land the EU's 2020 forest map does not class as forest, which a Hansen-only check counts as loss. The 20 codes are below.

On land that was…no loss sincelost 2021lost 2022lost 2023lost 2024lost 2025
forest in 2020FF·21F·22F·23F·24F·25
not forest in 2020NN·21N·22N·23N·24N·25
forest state unknown??·21?·22?·23?·24?·25
waterunknown cell

3 forest states × 6 loss states + water + unknown = 20 codes. The decision layer merges every N·year into plain N. Orange outline = loss after the cut-off. Unknown is its own value: a missing observation is never recorded as "no loss".

Where the last bytes come from technical

A plain GeoTIFF of the decision codes is already 8,532 B, 98.6% smaller than the originals, so most of the saving comes from deciding what to keep rather than from a codec. Plotline's encoding (separate bit-planes for land, water and forest, plus a sparse list of the few loss events) removes a further 28%, down to 6,146 B. The lossless "v1" store is retained as the audit tier in case the rule changes.

A learned codec selector lost to a fixed setting on a sealed holdout region and was rejected.

Deep dive: how Plotline works →

4 · How a plot gets its answer

From outline to answer

A cell belongs to a plot if the cell's centre falls inside the outline. This written rule is part of the decision: changing it changes answers.

SyntheticA made-up plot on a made-up grid
F forest in 2020N not forestW water·22 lost in 2022

Synthetic illustration; no real plot, cell or count.

5 · The proof

Identical answers, checked for every cell

Dropping data is acceptable only if nothing the decision reads is lost. An independent path rebuilds the code for every map cell straight from the original GeoTIFFs and compares it with the decision layer. The comparison covers all 744,732 cells, not a sample.

This covers every plot, not only the 17 supplied: each answer depends only on the codes of the selected cells, so if every cell's code matches, any boundary drawn in the region gets identical outputs under the same centre rule.

Synthetic coloursA 1,233 × 604 grid checks cell by cell
0of 744,732 cells identical
0of 17 supplied plots, every output
0of 9,829 fuzzed polygons
0mismatches

CheckEvery cell is rebuilt from the originals and compared.

The grid has the real chip's dimensions but not its data; colours are decorative. The 9,829 fuzzed polygons (holes, multipolygons, edges through cell centres, chip edges, water) exclude 171 invalid shapes that both paths rejected identically. "Break one cell" illustrates the negative controls, which all detected the change.

Also checkedAn independent validator re-derived the result with exact arithmetic and found 0 mismatches between fast and reference selection on 4,920 validator-generated geometries. It also found hardening defects (an unprotected header, an overflow path, level mix-ups, allocation amplification, missing content binding), each fixed behind a test that failed on the old code.
6 · The app

The Plotline app: five views

The Plotline app presents the evidence to a reviewer. It is map-first and draws the real Hansen and JRC grids in the browser.

Map

A year scrubber for 2001 to 2025, Hansen stacked with the JRC 2020 forest, and a plot view showing its cells and centres.

Cohort

All 17 plots by 25 years as a loss "barcode", with supplier declarations beside what the maps show.

Proof

The fidelity, storage and timing numbers, and the ladder of what was dropped and why.

Live query

A newly drawn boundary is answered by both the original and compact paths.

Receipts

Every answer carries layer versions, grid, rule and hashes so anyone can re-check it.

7 · The numbers

Results: identical answers, smaller storage, faster queries

Three claims are measured separately, each with its conditions stated beside the number.

Deep dive: the speed story →

Same · smaller · faster
Same answers744,732of 744,732 cells give an identical answer
Every cell744,732 / 744,732
Supplied plots17 / 17
Fuzzed polygons9,829 / 9,829
Smaller99% smaller623 KB → 6.1 KB on the sample; 422 MB → 8.2 MB on a whole tile
Sample region (34 × 17 km)
Original layers623,237 B
Decision layer6,146 B
Whole 10° × 10° tile (1.6 billion cells)
Original files421.9 MB
Decision layer8.2 MB
Faster44×registered plots, hot query. New boundaries: about 5×. The two are separate measures.
Registered plots (checked again and again)
Retained v1 path1× (baseline)
Plot index44×
Index, numpy-free83×
Answer tableabout 410×
New, never-seen boundaries
Fast exact selection5.2×
Batch of hundredsabout 16×*
log scale: each gridline is ten times the one before · *one call for the whole batch

Sample-region and whole-tile bars are linear; speed bars are log.

Design limits

  • Edge cells are all-or-nothing. Under the centre rule a cell counts for a plot when its centre lies inside the boundary; cells are about 0.076 ha. Partial edge coverage and the 0.5 ha minimum area are not modelled.
  • JRC is sampled once per Hansen cell, at the centre of its 3 × 3 block. The alternatives (majority, any forest) give the same decision for all 17 plots, but only one plot has mapped forest.
  • The layer is rule-specific. A changed rule or an added layer means re-deriving from the lossless audit tier: seconds per region, about 7 CPU-minutes per tile (estimated).
  • The largest speed-ups need registered plots. The 44× to 410× figures apply to plots registered in advance; a new boundary gets 5.2×.
  • Storage was measured on a whole tile and query timing on one region.
Speed phases: 24× to 44× history

The decision layers were benchmarked first: registered plots 24×, new boundaries 2.2×. Four phases followed with no change to any output, store byte or file format. Phase 1 sped up decoding, histogram reduction and polygon selection (31× and 3.0×). Phase 2 added a scalar scan, a batch evaluator and the numpy-free re-check (33× and 5.0×). Phase 3 sped up the shared reducer (44×). Phase 4 trimmed it again (numpy-free 65× to 83×; new boundaries read 5.2×) and ended with a research pass that found nothing that beats this approach for small exact plots on a CPU.

8 · What it found

Findings on the 17 supplied plots

With Hansen alone, 7 of the 17 plots show tree-cover loss after 2020. Hansen records any tree-cover loss on any land, and the region is rubber and oil-palm country. With the JRC 2020 forest map added, only P-0001 has mapped loss on mapped 2020 forest: 2 cells, in 2022, out of its 38 Hansen loss cells that year.

Deep dive: what the plots show →

Schematic17 plots, three filters

Filter

One dot per plot; positions are decorative. The six other plots with Hansen loss after 2020 (P-0005, P-0006, P-0013, P-0014, P-0016, P-0018) had that loss only on land JRC did not map as forest in 2020. Counts are cells, not hectares.

What the supplier declared

Declared indication, no mapped loss

All 17 declare "Deforestation-free", checked against "Hansen Tree Cover Loss 2021-2023". Five (P-0003, P-0005, P-0008, P-0010, P-0013) record an "indication of deforestation", resolved by on-site verification. On that layer and the project's rule, none of those five shows loss in 2021 to 2023.

The reverse case

Declared none, map shows some

Three plots declared no indication but show 2021 to 2023 loss on the layer they cite: P-0001 (38 cells in 2022), P-0016 (1 in 2023), P-0018 (1 in 2021). Neither the older Hansen release nor a different boundary rule explains the gap.

ScopeThese are mapped observations under a written rule, not a legal compliance verdict. The wording is "mapped loss on mapped 2020 forest", not "deforestation found". The screen does not model EUDR's 0.5 ha minimum area, the "able to reach 5 m" test or protected areas.
Reading map

Other pages

Results are encoding and query evidence on a sample region, not legal compliance findings. Supplier plot boundaries and declaration rows are not shown; every plot drawn here is synthetic.