Fragmentation Metrics Change with Resolution
On this page
Problem statement
The fragmentation statistics from a 30 m forest map say the landscape has tens of thousands of patches; a study using 300 m data reports a few hundred for the same area. Edge density falls sevenfold; core area and the largest patch shift in ways that depend on how cells were aggregated. Neither study is wrong about its map. Fragmentation metrics describe the map at its resolution, and aggregating pixels merges patches, fills narrow gaps and smooths edges β systematically, in one direction.
Comparisons across studies, sensors or years with different data are only meaningful at a common resolution, and every fragmentation number should carry the pixel size it was computed at.
Quick answer
Aggregate the forest mask to several resolutions by majority and recompute the metrics:
import numpy as np, rasterio
from scipy import ndimage
with rasterio.open("rondonia_gfc.tif") as src: # Hansen stack, reading guide
treecover, lossyear, datamask = src.read()
f = (treecover > 30) & (datamask == 1) & (lossyear == 0) # forest in 2024
px_m = 0.00025 * 111_320 * np.cos(np.radians(-10.55)) # ~27.4 m: the GFC grid step here
def majority(mask, k):
h, w = (mask.shape[0] // k) * k, (mask.shape[1] // k) * k
return mask[:h, :w].reshape(h // k, k, w // k, k).mean(axis=(1, 3)) >= 0.5
def metrics(mask, cell_m):
lab, n = ndimage.label(mask, structure=np.ones((3, 3)))
sizes = np.bincount(lab.ravel())[1:]
edge = mask & ~ndimage.binary_erosion(mask)
core = ndimage.binary_erosion(mask, iterations=max(1, int(round(100 / cell_m))))
ha = cell_m ** 2 / 1e4
return (mask.mean(), n, sizes.max() / mask.sum(),
edge.sum() * cell_m / (mask.size * ha), core.sum() / mask.sum())
print(f"{'cell':>6}{'forest':>8}{'patches':>9}{'largest':>9}{'edge m/ha':>11}{'core':>7}")
for k in (1, 2, 3, 5, 10):
m = f if k == 1 else majority(f, k)
share, n, lp, ed, core = metrics(m, px_m * k)
print(f"{px_m * k:>5.0f}m{share:>8.0%}{n:>9,}{lp:>9.0%}{ed:>11.1f}{core:>7.0%}")
cell forest patches largest edge m/ha core
27m 26% 25,871 4% 37.3 28%
55m 28% 10,312 9% 25.7 31%
82m 24% 6,339 6% 16.4 44%
137m 22% 2,684 6% 10.2 37%
274m 19% 892 5% 5.3 25%
Forest share moves by seven points between the finest and coarsest cells; the patch count falls nearly thirtyfold and edge density sevenfold, while core area moves up and down with no clear direction. The metrics most used to describe fragmentation are the ones most sensitive to resolution.
Record resolution, compare at one, prefer robust measures
1. Record the resolution with every metric
Patch counts and edge densities are meaningless without the pixel size that produced them.
2. Compare at a common resolution
Aggregate the finer dataset to the coarser one's resolution, with the same rule (majority is common), before comparing.
3. Use resolution-robust measures where possible
Forest share and, to a lesser extent, the largest patch index are more stable than patch counts and edge density.
4. Keep the core-area edge depth in metres
When cells get bigger, the number of erosion steps for a 100 m edge depth changes; express the depth in metres and convert per resolution. At coarse resolution it cannot go below one cell.
5. Choose the resolution from the process
Edge effects act over tens to hundreds of metres; a 300 m map cannot resolve them. Use the finest resolution that the data support and the question needs.
Aggregation rules and vanishing gaps
The data used on this page
The RondΓ΄nia window used throughout the forest guides (63.0Β°β62.5Β°W, 10.8Β°β10.3Β°S): Hansen Global Forest Change v1.12 β tree cover in 2000, the year of loss, and the data mask β read from Google's storage into a three-band rondonia_gfc.tif, as in the Hansen reading guide.
import os
import rasterio
from rasterio.windows import from_bounds
W, S, E, N = -63.0, -10.8, -62.5, -10.3
V = "GFC-2024-v1.12"
if not os.path.exists("rondonia_gfc.tif"):
bands = []
for name in ("treecover2000", "lossyear", "datamask"):
url = f"https://storage.googleapis.com/earthenginepartners-hansen/{V}/Hansen_{V}_{name}_10S_070W.tif"
with rasterio.open(url) as src:
window = from_bounds(W, S, E, N, src.transform)
bands.append(src.read(1, window=window))
profile = src.profile | {"count": 3, "width": bands[0].shape[1], "height": bands[0].shape[0],
"transform": src.window_transform(window), "compress": "deflate",
"driver": "GTiff", "tiled": False}
profile.pop("blockxsize", None)
profile.pop("blockysize", None)
with rasterio.open("rondonia_gfc.tif", "w", **profile) as dst:
for i, band in enumerate(bands, start=1):
dst.write(band, i)
with rasterio.open("rondonia_gfc.tif") as src:
print(src.count, src.shape, src.crs, src.res)
3 (2000, 2000) EPSG:4326 (0.00025, 0.00025)
Example 1 β the aggregation rule
k = 5
maj = majority(f, k)
h, w = (f.shape[0] // k) * k, (f.shape[1] // k) * k
blocks = f[:h, :w].reshape(h // k, k, w // k, k)
any_forest = blocks.any(axis=(1, 3))
all_forest = blocks.all(axis=(1, 3))
for name, m in (("any forest pixel", any_forest), ("majority", maj), ("all pixels forest", all_forest)):
share, n, lp, ed, core = metrics(m, px_m * k)
print(f"{name:<18} forest {share:5.0%}, patches {n:>6,}, core {core:4.0%}")
any forest pixel forest 61%, patches 758, core 50%
majority forest 22%, patches 2,684, core 37%
all pixels forest forest 9%, patches 1,008, core 43%
At the same coarse resolution, the rule that decides whether a cell is forest changes even the forest share. At 137 m the forest share ranges from 9% to 61% depending on the rule. "Aggregated to 137 m" is not a complete description without it.
Example 2 β narrow gaps disappear
narrow = ~f & ndimage.binary_closing(f, iterations=2) # gaps a 2-pixel closing fills
print(f"non-forest pixels in gaps up to ~4 pixels wide: {narrow.sum() / (~f).sum():.1%} of non-forest")
print(f"forest share after closing those gaps: {(f | narrow).mean():.1%} (was {f.mean():.1%})")
non-forest pixels in gaps up to ~4 pixels wide: 7.0% of non-forest
forest share after closing those gaps: 31.1% (was 25.9%)
Narrow clearings β tracks, stream corridors, the fishbone's side roads β are a small share of non-forest area and a large share of what breaks patches apart. They vanish first when resolution coarsens, which is why patch counts collapse.
Example 3 β the same change, two resolutions
f2000 = (treecover > 30) & (datamask == 1)
for k in (1, 10):
a = f2000 if k == 1 else majority(f2000, k)
b = f if k == 1 else majority(f, k)
na, nb = metrics(a, px_m * k)[1], metrics(b, px_m * k)[1]
print(f"{px_m * k:>4.0f} m: patches 2000 {na:>6,} β 2024 {nb:>6,} ({nb / na:.1f}Γ)")
27 m: patches 2000 14,079 β 2024 25,871 (1.8Γ)
274 m: patches 2000 397 β 2024 892 (2.2Γ)
Even the direction and size of change in patch count differ between resolutions. A trend in fragmentation is only a trend at one resolution.
Why aggregation reduces fragmentation
Why aggregation reduces fragmentation
A coarse cell is forest or not as a whole. Small clearings inside a forested cell disappear; small forest patches inside a cleared cell disappear; narrow gaps between patches are filled when neighbouring cells are mostly forest. Every one of those operations reduces patch counts and edge length.
Why forest share is more robust
Majority aggregation preserves proportions approximately when landscapes are not dominated by features near the cell size. Structure measures depend on the arrangement of pixels, which is exactly what aggregation changes.
Why the choice of resolution is scientific, not technical
Processes act at particular scales: microclimate edges over 50β100 m, animal movement over hundreds of metres to kilometres. A metric computed at a resolution coarser than the process cannot describe it; one computed much finer may add noise.
Sensors, mapping units and connectivity
- Sensor resolution: comparing Landsat-based (30 m) with MODIS-based (250β500 m) fragmentation is comparing two maps, not two landscapes.
- Minimum mapping unit acts like a resolution change for patch statistics.
- Connectivity rule (4 or 8 neighbours) interacts with resolution; keep both fixed.
- Scaling relationships between metrics and resolution are landscape-specific; they cannot be corrected with a universal factor.
- FRAGSTATS documentation discusses scale dependence of each metric.
Internal links
- How to measure forest fragmentation β the metrics
- Forest fragmentation explained β what they mean
- Raster resampling explained β aggregation rules
- Modifiable areal unit problem explained β scale effects in general
- How to compare land cover products for forest β products at different resolutions
- Forest threshold changes everything β the other definition that moves metrics
FAQ
Why do fragmentation metrics differ between studies?
Different resolutions, aggregation rules, connectivity rules and thresholds.
Which metrics are most sensitive to resolution?
Patch counts, edge density and core area. Forest share is the most robust.
How do I compare fragmentation from 30 m and 250 m data?
Aggregate the 30 m map to 250 m with a stated rule and compare at that resolution.
What aggregation rule should I use?
Majority is common; state it, because "any" and "all" rules give different results.
Can I correct metrics for resolution?
Not with a general factor; the relationship depends on the landscape.
What resolution should I use?
The finest the data support that matches the scale of the process you study.