Skip to content

Fragmentation

Habitat density in moving windows, fragmentation classes, landscape mosaics, contagion, clustering, entropy and window statistics of percentage maps. Tutorial: fragmentation.

Habitat density

fad

Forest Area Density (FAD) & Multiscale Fragmentation Analysis.

Reimplements GuidosToolbox (GTB) FAD and FAD-Multiscale: - fad(): Single-scale local density and fragmentation classification. - fad_multiscale(): Multi-scale observation analysis across 5 scales (7x7, 13x13, 27x27, 81x81, 243x243) and composite average map.

References

Vogt, P., et al. (2007). Mapping landscape corridors. Ecological Indicators, 7(2), 481-488. Riitters, K., et al. (2002). Fragmentation of continental United States forests. Ecosystems, 5(8), 815-822.

FADResult dataclass

FADResult(
    density: DataArray | ndarray,
    raw_density: DataArray | ndarray,
    classes: DataArray | ndarray,
    window_size: int,
    stats: DataFrame,
)

Bases: Saveable

Result of single-scale FAD analysis (fad()).

Examples:

>>> import landschaft as ls
>>> result = ls.fad(ls.load_raster(ls.example_data("input")), window_size=13)
>>> result.window_size, result.density.shape
(13, (1000, 1000))

summary

summary() -> str

Formatted text summary of fragmentation statistics.

FADMultiscaleResult dataclass

FADMultiscaleResult(
    multiscale_map: DataArray | ndarray,
    classes: DataArray | ndarray,
    single_scales: dict[int, DataArray | ndarray],
    raw_single_scales: dict[int, DataArray | ndarray],
    window_sizes: list[int],
    stats: DataFrame,
)

Bases: Saveable

Result of multiscale FAD analysis (fad_multiscale()).

Examples:

>>> import landschaft as ls
>>> result = ls.fad_multiscale(ls.load_raster(ls.example_data("input")), window_sizes=(7, 27))
>>> result.window_sizes
[7, 27]

summary

summary() -> str

Formatted text summary of multiscale fragmentation statistics.

fad

fad(
    data: DataArray | ndarray,
    window_size: int = 7,
    *,
    threads: int = 0,
    n_threads: int | None = None,
) -> FADResult

Compute Forest Area Density (FAD) for a single window size.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D raster coded 0=missing, 1=background, 2=foreground.

required
window_size int

Moving window size (must be an odd integer >= 3).

7
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
FADResult

Result object containing density map, 6-class map, and statistics table.

Examples:

>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> result = ls.fad(forest, window_size=27)
>>> float(result.stats["average_fad"].iloc[0])
73.54
>>> _ = ls.write_raster(result.density, "fad27.tif")   # with the colours of the six classes

fad_multiscale

fad_multiscale(
    data: DataArray | ndarray,
    window_sizes: Sequence[int] = (7, 13, 27, 81, 243),
    *,
    threads: int = 0,
    n_threads: int | None = None,
) -> FADMultiscaleResult

Compute Multiscale Forest Area Density (FAD-MS) matching GuidosToolbox.

Evaluates local forest density across 5 observation scales (or user-defined window sizes) using high-performance C++ box integrals, classifies pixels into 6 fragmentation categories, and generates the multi-scale composite map.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D raster coded 0=missing, 1=background, 2=foreground, and optionally 3=special background and 4=non-fragmenting special background (treated as missing when measuring density), as in GTB. The maps code them 105 and 106 (and background 101, missing 102).

required
window_sizes sequence of int

List of odd window sizes.

(7, 13, 27, 81, 243)
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
FADMultiscaleResult

Result containing composite map, single scale maps, and statistics table.

Examples:

>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> result = ls.fad_multiscale(forest)
>>> list(result.stats["window_size"])
[7, 13, 27, 81, 243, 'Average']
>>> sorted(result.single_scales)
[7, 13, 27, 81, 243]

FADResult dataclass

FADResult(
    density: DataArray | ndarray,
    raw_density: DataArray | ndarray,
    classes: DataArray | ndarray,
    window_size: int,
    stats: DataFrame,
)

Bases: Saveable

Result of single-scale FAD analysis (fad()).

Examples:

>>> import landschaft as ls
>>> result = ls.fad(ls.load_raster(ls.example_data("input")), window_size=13)
>>> result.window_size, result.density.shape
(13, (1000, 1000))

summary

summary() -> str

Formatted text summary of fragmentation statistics.

fad_multiscale

fad_multiscale(
    data: DataArray | ndarray,
    window_sizes: Sequence[int] = (7, 13, 27, 81, 243),
    *,
    threads: int = 0,
    n_threads: int | None = None,
) -> FADMultiscaleResult

Compute Multiscale Forest Area Density (FAD-MS) matching GuidosToolbox.

Evaluates local forest density across 5 observation scales (or user-defined window sizes) using high-performance C++ box integrals, classifies pixels into 6 fragmentation categories, and generates the multi-scale composite map.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D raster coded 0=missing, 1=background, 2=foreground, and optionally 3=special background and 4=non-fragmenting special background (treated as missing when measuring density), as in GTB. The maps code them 105 and 106 (and background 101, missing 102).

required
window_sizes sequence of int

List of odd window sizes.

(7, 13, 27, 81, 243)
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
FADMultiscaleResult

Result containing composite map, single scale maps, and statistics table.

Examples:

>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> result = ls.fad_multiscale(forest)
>>> list(result.stats["window_size"])
[7, 13, 27, 81, 243, 'Average']
>>> sorted(result.single_scales)
[7, 13, 27, 81, 243]

FADMultiscaleResult dataclass

FADMultiscaleResult(
    multiscale_map: DataArray | ndarray,
    classes: DataArray | ndarray,
    single_scales: dict[int, DataArray | ndarray],
    raw_single_scales: dict[int, DataArray | ndarray],
    window_sizes: list[int],
    stats: DataFrame,
)

Bases: Saveable

Result of multiscale FAD analysis (fad_multiscale()).

Examples:

>>> import landschaft as ls
>>> result = ls.fad_multiscale(ls.load_raster(ls.example_data("input")), window_sizes=(7, 27))
>>> result.window_sizes
[7, 27]

summary

summary() -> str

Formatted text summary of multiscale fragmentation statistics.

Fragmentation at a fixed scale

fos

Fragmentation at a Fixed Observation Scale (GuidosToolbox Fragmentation -> FOS).

For every foreground pixel, the connectivity of the foreground in a moving window of the chosen size, by one of three methods:

  • FAD, foreground area density (share of foreground in the window);
  • FED, foreground edge density (GraySpatCon "clustering": mean of the pixel pairs of the window);
  • FAC, foreground area clustering (share of foreground-foreground pairs; binary maps only).

The input is either a binary map (0 missing, 1 background, 2 foreground, 3/4 special background) or a grayscale map (0-100 % cover, 255 missing, 103/104 special background) whose foreground is the cover at or above a threshold. Results are reported in 5 or 6 fragmentation classes, as by GTB. The maps and statistics are those of GTB's batch FOS.

FOSResult dataclass

FOSResult(
    map: DataArray | ndarray,
    stats: DataFrame,
    histogram: ndarray,
    method: str,
    classes: int,
    window_size: int,
    grayscale: bool,
    gray_threshold: int | None,
    connectivity: int,
    pixel_resolution: float,
    fg_area: int,
    reporting_area: int,
    fg_area_pct: float,
    n_patches: int,
    average_patch_size: float,
    eca: float,
    coherence: float,
    coherence_ru: float,
    average: float,
    avcon: float,
)

Bases: Saveable

Result of fos().

Attributes:

Name Type Description
map DataArray | ndarray

uint8 FOS map: 0-100 connectivity [%] of the foreground pixels, 101 background, 102 missing, 105 special background, 106 non-fragmenting special background.

stats DataFrame

Share [%] of the foreground in each fragmentation class.

histogram ndarray

Share [%] of the foreground pixels with each value 0..100.

observation_scale_ha property

observation_scale_ha: float

Window area in hectares.

summary

summary() -> str

Report in the layout of GTB's fos.txt.

fos

fos(
    data,
    window_size: int = 27,
    method: str = "FAD",
    classes: int = 6,
    *,
    connectivity: int = 8,
    grayscale: bool | None = None,
    gray_threshold: int = 30,
    pixel_resolution: float = 25.0,
    n_threads: int = 0,
) -> FOSResult

Fragmentation at a Fixed Observation Scale (GTB FOS).

Parameters:

Name Type Description Default
data

Binary map (0 missing, 1 background, 2 foreground, 3/4 special background) or grayscale map (0-100, 255 missing, 103/104 special background); array, DataArray or raster path.

required
window_size int

Side of the square moving window [pixels], odd (GTB offers 3-243).

27
method str

"FAD", "FED" or "FAC" (binary maps only).

'FAD'
classes int

5 or 6 reporting classes (6 separates intact = 100 from interior).

6
connectivity int

Foreground connectivity for the patch statistics (8 or 4).

8
grayscale bool | None

Treat the map as grayscale; default: when its maximum exceeds 4 (GTB).

None
gray_threshold int

Grayscale maps: pixels with a value of at least this are foreground.

30
pixel_resolution float

Pixel size [m], for the observation scale in hectares.

25.0
n_threads int

Threads (0: all cores but one).

0

Returns:

Type Description
FOSResult

The class map (map), the share of each class (stats), a histogram of the values and patch statistics of the whole map (n_patches, average_patch_size, eca, coherence, average, avcon); summary() prints the report.

Examples:

>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> result = ls.fos(forest, window_size=27, method="FAC", classes=5)
>>> list(result.stats["class"])
['Rare', 'Patchy', 'Transitional', 'Dominant', 'Interior']
>>> result.n_patches
2850

A percentage map (grayscale): pixels of at least 30% are habitat.

>>> cover = ls.load_raster(ls.example_data("gscinput"))
>>> gray = ls.fos(cover, window_size=27, gray_threshold=30)
>>> gray.grayscale
True

FOSResult dataclass

FOSResult(
    map: DataArray | ndarray,
    stats: DataFrame,
    histogram: ndarray,
    method: str,
    classes: int,
    window_size: int,
    grayscale: bool,
    gray_threshold: int | None,
    connectivity: int,
    pixel_resolution: float,
    fg_area: int,
    reporting_area: int,
    fg_area_pct: float,
    n_patches: int,
    average_patch_size: float,
    eca: float,
    coherence: float,
    coherence_ru: float,
    average: float,
    avcon: float,
)

Bases: Saveable

Result of fos().

Attributes:

Name Type Description
map DataArray | ndarray

uint8 FOS map: 0-100 connectivity [%] of the foreground pixels, 101 background, 102 missing, 105 special background, 106 non-fragmenting special background.

stats DataFrame

Share [%] of the foreground in each fragmentation class.

histogram ndarray

Share [%] of the foreground pixels with each value 0..100.

observation_scale_ha property

observation_scale_ha: float

Window area in hectares.

summary

summary() -> str

Report in the layout of GTB's fos.txt.

Landscape mosaic

landscape_mosaic

landscape_mosaic(
    data: DataArray | ndarray,
    window_size: int = 27,
    output_classes: int = 19,
    *,
    threads: int = 0,
    n_threads: int | None = None,
) -> DataArray | ndarray

Compute Landscape Mosaic (LM) moving-window metric matching GuidosToolbox batch_lm.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D raster coded: - 0: Missing - 1: Agriculture (or Class 1) - 2: Forest / Natural (or Class 2) - 3: Developed / Antropic (or Class 3)

required
window_size int

Size of the square moving convolution window. Must be an odd integer >= 3. Common GTB observation scales: 3, 5, 7, 9, 11, 13, 27, 81, 243.

27
output_classes int

Number of output classes: - 19: Standard GuidosToolbox 19-class landscape mosaic. - 103: Detailed 103-class barycentric triangle cells.

19
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
DataArray or ndarray

Landscape mosaic raster (uint8).

Examples:

Land cover in three classes (1 agriculture, 2 natural, 3 developed) from the CORINE example:

>>> import landschaft as ls
>>> clc = ls.load_raster(ls.example_data("clc"))
>>> three = ls.reclassify(clc, [(1, 12, 3), (12, 23, 1), (23, 35, 2)], others=0)   # developed, agriculture, natural
>>> mosaic = ls.landscape_mosaic(three, window_size=27)
>>> ls.LM19_CLASSES[17]
'Pure Forest (100%)'

landscape_mosaic_stats

landscape_mosaic_stats(
    data: DataArray | ndarray,
    window_size: int = 27,
    *,
    is_lm103: bool = False,
    threads: int = 0,
    n_threads: int | None = None,
) -> DataFrame

Compute Landscape Mosaic 103-class frequency distribution matching GTB heatmap.csv.

Parameters:

Name Type Description Default
data DataArray or ndarray

Either: - 2D landcover raster with values in {0, 1, 2, 3} (if is_lm103=False). - Precomputed 103-class landscape mosaic raster (if is_lm103=True).

required
window_size int

Window size used when computing 103-class LM from raw landcover.

27
is_lm103 bool

Set to True if data is already a 103-class LM raster.

False
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
DataFrame

DataFrame with columns class_code, pixel_count, percentage, recode_class_19 and recode_name_19 (the 19-class code and name).

Examples:

>>> import landschaft as ls
>>> clc = ls.load_raster(ls.example_data("clc"))
>>> three = ls.reclassify(clc, [(1, 12, 3), (12, 23, 1), (23, 35, 2)], others=0)   # developed, agriculture, natural
>>> table = ls.landscape_mosaic_stats(three, window_size=27)
>>> list(table.columns)
['class_code', 'pixel_count', 'percentage', 'recode_class_19', 'recode_name_19']

landscape_mosaic_heatmap

landscape_mosaic_heatmap(
    data,
    window_size: int = 27,
    *,
    is_lm103: bool = False,
    window: str | None = None,
    threads: int = 0,
) -> LMHeatmap

Heatmap of a Landscape Mosaic, as GTB computes it.

Parameters:

Name Type Description Default
data

Land-cover map coded 0 = missing, 1 = agriculture, 2 = forest, 3 = developed (array, DataArray or raster path), or with is_lm103=True a 103-class LM map (missing = 0).

required
window_size int

LM window size for a land-cover map.

27
is_lm103 bool

data already is a 103-class LM map.

False
window str | None

Window label for the CSV title (default: window_size; GTB writes "multiscale" for multiscale LM).

None
threads int

OpenMP threads for the LM (0: all cores but one).

0

Returns:

Type Description
LMHeatmap

The share of the map (%) in each of the 103 cells of the composition triangle (values); to_csv(path) writes the table.

Examples:

>>> import landschaft as ls
>>> clc = ls.load_raster(ls.example_data("clc"))
>>> three = ls.reclassify(clc, [(1, 12, 3), (12, 23, 1), (23, 35, 2)], others=0)   # developed, agriculture, natural
>>> heat = ls.landscape_mosaic_heatmap(three, window_size=27)
>>> _ = heat.to_csv("heatmap.csv")

LMHeatmap dataclass

LMHeatmap(values: ndarray, window: str)

Bases: Saveable

Landscape Mosaic heatmap of one map (GTB *_heatmap.sav / .csv).

Attributes:

Name Type Description
values ndarray

GTB's heatmap vector hn: 256 single-precision entries, entry c = share [%] of LM class c among the non-missing pixels.

window str

Window size label used in the reports (e.g. "27" or "multiscale").

table property

table: DataFrame

Share [%] per LM class code, with its 19-class recode.

triangle property

triangle: ndarray

The heatmap as GTB's 12 x 21 triangle grid (NaN outside the triangle).

csv_text

csv_text() -> str

Content of GTB's heatmap.csv.

to_csv

to_csv(path: str | Path) -> Path

Write GTB's heatmap.csv.

Landscape mosaic classes

ls.LM19_CLASSES names the 19 classes: 1–3 one type above 60% (agriculture, developed, forest), 4–12 two or three types in a given order of dominance, 13–16 mosaics without a dominant type, 17–19 pure (100%) forest, agriculture or developed, and 0 missing.

Contagion, clustering and entropy

contagion_pff

contagion_pff(
    data: DataArray | ndarray,
    window_size: int = 7,
    *,
    as_byte: bool = True,
    apply_gtb_mask: bool = True,
    threads: int = 0,
    n_threads: int | None = None,
) -> DataArray | ndarray

Compute moving-window Contagion (Pff) matching GuidosToolbox batch_pff.

Contagion (Pff) measures the conditional probability that a pixel adjacent to a foreground pixel is also foreground:

Pff = E_ff / E_f*

where E_ff is the count of foreground-foreground joins and E_f* is the count of joins involving foreground and any non-missing pixel.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D binary raster coded 0=missing, 1=background, 2=foreground.

required
window_size int

Odd integer >= 3 (e.g. 7, 13, 27).

7
as_byte bool

If True, returns uint8 with GTB byte scaling: - 0..100: Contagion percentage - 101: Background mask - 102: Missing mask If False, returns float32 with values in [0.0, 1.0] and NaN for background/missing.

True
apply_gtb_mask bool

If True, applies background (101) and missing (102) masks.

True
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
DataArray or ndarray

Contagion raster (uint8 or float32).

Examples:

>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> pff = ls.contagion_pff(forest, window_size=13)                  # 0-100, 101/102 masks
>>> probability = ls.contagion_pff(forest, window_size=13, as_byte=False)   # 0-1, NaN off habitat

clustering_fac

clustering_fac(
    data: DataArray | ndarray,
    window_size: int = 7,
    *,
    as_byte: bool = True,
    apply_gtb_mask: bool = True,
    threads: int = 0,
    n_threads: int | None = None,
) -> DataArray | ndarray

Compute moving-window Foreground Area Clustering (FAC) matching GuidosToolbox batch_fac.

Foreground Area Clustering (FAC) measures the proportion of all non-missing pixel edges in the moving window that are foreground self-joins:

FAC = E_ff / E_total

Parameters:

Name Type Description Default
data DataArray or ndarray

2D binary raster coded 0=missing, 1=background, 2=foreground.

required
window_size int

Odd integer >= 3 (e.g. 7, 13, 27).

7
as_byte bool

If True, returns uint8 with GTB byte scaling: - 0..100: Clustering percentage - 101: Background mask - 102: Missing mask If False, returns float32 with values in [0.0, 1.0] and NaN for background/missing.

True
apply_gtb_mask bool

If True, applies background (101) and missing (102) masks.

True
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
DataArray or ndarray

Clustering raster (uint8 or float32).

Examples:

>>> import landschaft as ls
>>> fac = ls.clustering_fac(ls.load_raster(ls.example_data("input")), window_size=13, as_byte=False)
>>> float(fac.max()) <= 1.0
True

spatial_entropy

spatial_entropy(
    data: DataArray | ndarray, *, threads: int = 0, n_threads: int | None = None
) -> SpatialEntropyResult

Compute global normalized spatial joint directional entropy index.

Evaluates the frequency of transitions across 8 directions among foreground, background, and missing pixels, normalized to [0, 100]% matching GuidosToolbox entropy_mspainp.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D raster coded 0=missing, 1=background, 2=foreground.

required
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
SpatialEntropyResult

Result containing normalized entropy index and directional transition histogram.

Examples:

>>> import landschaft as ls
>>> result = ls.spatial_entropy(ls.load_raster(ls.example_data("input")))
>>> round(result.entropy_index, 1)
32.5

SpatialEntropyResult dataclass

SpatialEntropyResult(entropy_index: float, histogram: ndarray)

Bases: Saveable

Result of global spatial entropy analysis (spatial_entropy()).

summary

summary() -> str

Formatted text summary of entropy.

spatial_entropy_map

spatial_entropy_map(
    data: DataArray | ndarray,
    window_size: int = 50,
    *,
    threads: int = 0,
    n_threads: int | None = None,
) -> DataArray | ndarray

Compute moving-window spatial entropy map matching GuidosToolbox batch_ent.

Parameters:

Name Type Description Default
data DataArray or ndarray

2D raster coded 0=missing, 1=background, 2=foreground.

required
window_size int

Size of moving convolution window.

50
threads int

Threads of the compiled code (also as n_threads); 0: all cores but one.

0

Returns:

Type Description
DataArray or ndarray

Local spatial entropy raster in [0, 100]%.

Examples:

>>> import landschaft as ls
>>> local = ls.spatial_entropy_map(ls.load_raster(ls.example_data("input")), window_size=51)
>>> local.shape
(1000, 1000)

Percentage maps

gray_spatial_convolution

gray_spatial_convolution(
    data: DataArray | ndarray | str | Path,
    metric: str = "mean",
    window_size: int = 7,
    mask_missing: bool = True,
    n_threads: int = 0,
) -> DataArray | ndarray

Compute moving-window spatial convolution metric over a grayscale raster.

Parameters:

Name Type Description Default
data xarray.DataArray, numpy.ndarray, str, or Path

Continuous grayscale raster (byte values 0..100, 255 = missing/nodata).

required
metric str

Spatial metric to compute within each moving window. Supported metrics: - "mean" (or "average"): window mean - "median": window median - "majority" (or "mode"): most frequent value in window - "min": window minimum - "max": window maximum - "range": max - min - "stddev" (or "std"): standard deviation - "shannon_diversity": Shannon entropy index H' = -sum(p * ln(p)) - "shannon_evenness": Pielou's evenness J' = H' / ln(S) - "diagonal_contagion": contagion index over the gray spectrum

"mean"
window_size int

Side length of the square sliding window in pixels (must be odd, >= 3).

7
mask_missing bool

If True, pixels that are missing (value 255) remain 255 (or NaN) in the output.

True
n_threads int

Number of OpenMP worker threads (0: all cores but one).

0

Returns:

Type Description
DataArray or ndarray

Floating-point (float32) raster of the computed spatial convolution metric.

Examples:

>>> import landschaft as ls
>>> cover = ls.load_raster(ls.example_data("gscinput"))   # 0-100 %, 255 missing
>>> mean = ls.gray_spatial_convolution(cover, metric="mean", window_size=7)
>>> spread = ls.gray_spatial_convolution(cover, metric="stddev", window_size=9)
>>> str(mean.dtype)
'float32'