Skip to content

Landscape metrics

Patch, class and landscape metrics of categorical maps, their sampling in plots, windows and zones, time series, spatial signatures, plots and utilities. Tutorial: landscape metrics.

Metrics of a map

Landscape

Landscape(
    data,
    *,
    res=None,
    nodata=None,
    directions: int = 8,
    count_boundary: bool = False,
    consider_boundary: bool = False,
    edge_depth: int = 1,
    cell_center: bool = False,
    classes_max: float | None = None,
    neighbourhood: int = 4,
    ordered: bool = True,
    base: str = "log2",
    band: int | None = None,
    block_rows: int | str | None = None,
    n_threads: int | None = None,
    convention: str = "landscapemetrics",
    search_radius: float | None = None,
    connect_threshold: float | None = None,
    contrast=None,
    similarity=None,
    boundary_contrast: float = 0.0,
)

Categorical raster prepared for landscape metrics.

Patches and adjacencies are computed once, on first use, and shared by every metric. Metric names are the landscapemetrics / FRAGSTATS abbreviations (see list_metrics()).

Parameters:

Name Type Description Default
data

Categorical map: a file path, an xarray.DataArray (cell size, NA value and CRS are taken from it), a numpy array or a masked array. Floats are truncated to integers; NaN is NA.

required
res

Cell size (x, y) (or one number) in map units. Default: from the raster, else 1.

None
nodata

Value treated as NA (default: the raster's nodata value).

None
directions int

Patch connectivity: 8 (queen's case, default) or 4 (rook's case).

8
count_boundary bool

Count the landscape boundary (and NA cells) as edge in te/ed.

False
consider_boundary bool

Core-area options (cells at the boundary may be core; distance in cells from the patch edge).

False
edge_depth bool

Core-area options (cells at the boundary may be core; distance in cells from the patch edge).

False
cell_center bool

gyrate: use the patch cell nearest to the centroid.

False
classes_max float | None

Potential number of classes, for rpr.

None
neighbourhood int

Complexity metrics (ent, condent, joinent, mutinf, relmutinf): 4 or 8 neighbours, ordered pairs, and the logarithm base ("log2", "log" or "log10").

4
ordered int

Complexity metrics (ent, condent, joinent, mutinf, relmutinf): 4 or 8 neighbours, ordered pairs, and the logarithm base ("log2", "log" or "log10").

4
base int

Complexity metrics (ent, condent, joinent, mutinf, relmutinf): 4 or 8 neighbours, ordered pairs, and the logarithm base ("log2", "log" or "log10").

4
band int | None

Band to read from multi-band files.

None
block_rows int | str | None

Out-of-core computation for rasters larger than memory: data must be a file, read by blocks of this many rows (rounded up to a multiple of 64; "auto": 64 rows, at least 32,768 cells). Small blocks are worked on side by side, one per thread (about 64 rows per thread in memory, at most 32 million cells); a larger block alone, on every thread. Only those blocks and the patch tables stay in memory. Results are identical to the in-memory ones; labels and to_raster give lazy rasters, computed and written block by block.

None
n_threads int | None

Threads of the C++ core (default: all cores but one).

None
convention str

"landscapemetrics" (default) or "fragstats": with non-square cells, left/right cell sides are as long as the cell height (the physical perimeter; landscapemetrics takes the width); dcore_* summarise the sizes (ha) of the disjunct core areas instead of their number per patch; shape is the raster SHAPE (perimeter over the smallest perimeter of as many cells); _sd and _cv use the standard deviation over n (landscapemetrics: R's sd(), over n - 1); the circle encloses the whole patch (landscapemetrics: the corners of its first and last rows and columns only). Cells that are not square keep shape and lsi as .25 perimeter / sqrt(area), as pylandstats does.

'landscapemetrics'
search_radius float | None

prox and simi (FRAGSTATS extras): patches within this distance, in map units (NaN without it).

None
connect_threshold float | None

connect: patches of a class within this distance are connected.

None
contrast

Edge contrast weights (0-1) between classes for econ, cwed and teci: a DataFrame (index and columns = class values), a dict {(a, b): w} or {a: {b: w}} (symmetric; missing pairs give NaN), and the weight of edges to NA and the map edge (default 0).

None
boundary_contrast

Edge contrast weights (0-1) between classes for econ, cwed and teci: a DataFrame (index and columns = class values), a dict {(a, b): w} or {a: {b: w}} (symmetric; missing pairs give NaN), and the weight of edges to NA and the map edge (default 0).

None
similarity

Similarity weights between classes for simi, as contrast (default: 1 within a class, 0 between classes, i.e. simi = prox).

None
Notes

Metric names are landscapemetrics' abbreviations; FRAGSTATS abbreviations in capitals ("SHDI") and the full names of pylandstats / FRAGSTATS ("shannon_diversity_index", "total_area") are accepted too.

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> lc = ls.Landscape(landcover)
>>> lc.n_patches
967
>>> lc.class_metrics(["ca", "np"]).head(2)
   class         ca     np
0      1    174.500    2.0
1      2  27058.125  201.0
>>> float(lc.landscape_metrics(["shdi"])["shdi"].iloc[0])
1.8096...
>>> long = lc.metrics(type="aggregation metric")   # long format, like calculate_lsm

classes property

classes: ndarray

Class values present in the map, in increasing order.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> lc.classes[:5].tolist()
[1, 2, 3, 4, 5]

n_patches property

n_patches: int

Number of patches of all classes.

Examples:

>>> import landschaft as ls
>>> ls.Landscape(ls.load_raster(ls.example_data("clc"))).n_patches
967

patch_classes cached property

patch_classes: ndarray

Class value of every patch (patch id = position + 1).

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> lc.patch_classes[:3].tolist()
[1, 1, 2]

extent property

extent: tuple[float, float, float, float]

Extent of the raster: (xmin, xmax, ymin, ymax) in map units.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> xmin, xmax, ymin, ymax = lc.extent
>>> xmax - xmin
50000.0

out_of_core property

out_of_core: bool

Whether the raster is read by blocks (block_rows).

Examples:

>>> import landschaft as ls
>>> ls.Landscape(ls.example_data("clc"), block_rows=256).out_of_core
True

patch_metrics

patch_metrics(metrics: Iterable[str] | str | None = None) -> DataFrame

Patch-level metrics, one row per patch.

Parameters:

Name Type Description Default
metrics Iterable[str] | str | None

Metric names (default: all patch metrics). Upper-case and full names are accepted; the columns keep the names as given.

None

Returns:

Type Description
DataFrame

class, id and one column per metric.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> table = lc.patch_metrics(["area", "shape"])
>>> table.columns.tolist()
['class', 'id', 'area', 'shape']

class_metrics

class_metrics(
    metrics: Iterable[str] | str | None = None, *, extras: bool = False
) -> DataFrame

Class-level metrics, one row per class.

Parameters:

Name Type Description Default
metrics Iterable[str] | str | None

Metric names (default: all class metrics). Upper-case and full names are accepted; the columns keep the names as given.

None
extras bool

With the default selection, also the metrics landscapemetrics does not have (see list_metrics()).

False

Returns:

Type Description
DataFrame

class and one column per metric.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> lc.class_metrics(["ca", "pland", "np"]).columns.tolist()
['class', 'ca', 'pland', 'np']
>>> lc.class_metrics(["CA", "area_am"]).columns.tolist()
['class', 'CA', 'area_am']

landscape_metrics

landscape_metrics(
    metrics: Iterable[str] | str | None = None, *, extras: bool = False
) -> DataFrame

Landscape-level metrics, in a single row.

Parameters:

Name Type Description Default
metrics Iterable[str] | str | None

As in class_metrics().

None
extras Iterable[str] | str | None

As in class_metrics().

None

Returns:

Type Description
DataFrame

One row, one column per metric.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> lc.landscape_metrics(["ta", "np", "shdi"]).columns.tolist()
['ta', 'np', 'shdi']

metrics

metrics(
    level=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    *,
    extras: bool = False,
    full_name: bool = False,
) -> DataFrame

Selected metrics in long format, like landscapemetrics' calculate_lsm.

Selection as in [`list_metrics()`][landschaft.list_metrics] (all landscapemetrics
metrics by default; ``extras=True`` adds the FRAGSTATS ones).
``full_name=True`` adds the columns ``name``, ``type`` and
``function_name`` of each metric (as ``calculate_lsm(full_name = TRUE)``).
Returns
pandas.DataFrame
    Columns ``level``, ``class``, ``id``, ``metric``, ``value``
    (``class``/``id`` are missing where they do not apply). An all-NA
    map gives one missing value per metric, as in landscapemetrics.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> table = lc.metrics(level="landscape", type="diversity metric")
>>> table.columns.tolist()
['level', 'class', 'id', 'metric', 'value']
>>> sorted(table["metric"].astype(str))[:3]
['msidi', 'msiei', 'pr']

write_metrics

write_metrics(
    path,
    level=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    *,
    extras: bool = False,
    chunk_rows: int = 1000000,
    **kwargs,
) -> Path

Write metrics() (long format) to a file, without building it in memory.

One metric at a time, the patch metrics chunk by chunk; the same rows and types as metrics().

Parameters:

Name Type Description Default
path

.parquet (needs pyarrow), .csv or .csv.gz.

required
level

Selection, as in metrics().

None
metric

Selection, as in metrics().

None
name

Selection, as in metrics().

None
type

Selection, as in metrics().

None
what

Selection, as in metrics().

None
extras

Selection, as in metrics().

None
chunk_rows int

Patches per chunk.

1000000
**kwargs

Passed to the CSV or Parquet writer.

{}

Returns:

Type Description
Path

The file written.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> path = lc.write_metrics("metrics.csv.gz", level=["class", "landscape"])

write_patch_metrics

write_patch_metrics(
    path,
    metrics: Iterable[str] | str | None = None,
    *,
    chunk_rows: int = 1000000,
    **kwargs,
) -> Path

Write patch_metrics() to a file, chunk by chunk.

The table is never built in memory: for maps with tens of millions of patches.

Parameters:

Name Type Description Default
path

.parquet (one row group per chunk; needs pyarrow), .csv or .csv.gz.

required
metrics Iterable[str] | str | None

As in patch_metrics().

None
chunk_rows int

Patches per chunk.

1000000
**kwargs

Passed to the CSV or Parquet writer.

{}

Returns:

Type Description
Path

The file written.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> lc.write_patch_metrics("patches.csv", ["area", "perim"]).name
'patches.csv'

to_raster

to_raster(metric, level: str | None = None, path=None, **write_options)

Metric painted on the map (NaN where NA): a patch-level metric on its patches (landscapemetrics' spatialize_lsm), a class-level one on the cells of each class.

Parameters:

Name Type Description Default
metric

A metric name, or a list of them (then a dict {name: raster}).

required
level str | None

"patch" or "class"; by default the patch level if the metric exists there, else the class level.

None
path

Write the raster to this GeoTIFF (write_raster; with a list, a directory: one <metric>.tif each), and return the path(s). Out of core (block_rows) the raster is lazy (dask) and is written block by block.

None

Returns:

Type Description
(DataArray, ndarray, dict or path)

The raster (same form as the input; lazy out of core), a dict of them for a list of metrics, or the path(s) written.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> area = lc.to_raster("area")                  # every patch painted with its area (ha)
>>> pland = lc.to_raster("pland", level="class")
>>> path = lc.to_raster("shape", path="shape.tif")

Out of core, written block by block:

>>> big = ls.Landscape(ls.example_data("clc"), block_rows=256)
>>> path = big.to_raster("area", path="area.tif")

labels

labels(path=None, **write_options) -> DataArray | ndarray

Map of patch ids.

Out of core (block_rows) the map is lazy (dask): each block is read and labelled again when computed, so it can be larger than memory; with path it is written to the file block by block.

Parameters:

Name Type Description Default
path

Write the map to this GeoTIFF (write_raster) and return the path.

None
**write_options

Options of write_raster() (compress...).

{}

Returns:

Type Description
(DataArray, ndarray or path)

Patch ids 1..n (the id of the patch tables), 0 where NA; same grid as the input (lazy out of core), or the path written.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> ids = lc.labels()
>>> int(ids.max()) == lc.n_patches
True

A file read by blocks gets its map written by blocks:

>>> big = ls.Landscape(ls.example_data("clc"), block_rows=256)
>>> path = big.labels(path="patch_ids.tif")
>>> int(ls.load_raster(path).max()) == big.n_patches
True

patches

patches(classes=None) -> dict

The patches of each class, each class on a raster of its own.

Parameters:

Name Type Description Default
classes

Class values to return (default: all).

None

Returns:

Type Description
dict

{class: raster}: patch ids on the cells of the class, NaN elsewhere.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> forests = lc.patches(classes=[23, 24])
>>> sorted(forests)
[23, 24]

centroids

centroids(cell_center: bool = False) -> DataFrame

Centroid of every patch, like landscapemetrics' get_centroids.

The mean of the cell-centre coordinates of the patch (map coordinates, as terra computes them; the same bits as landscapemetrics). With cell_center the patch cell nearest to that point instead (several rows on ties, as landscapemetrics returns).

Returns:

Type Description
DataFrame

class, id, x, y.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> lc.centroids().columns.tolist()
['class', 'id', 'x', 'y']

nearest_neighbours

nearest_neighbours(return_id: bool = False) -> DataFrame

Distance from every patch to the nearest patch of its class, like landscapemetrics' get_nearestneighbour on the patches of each class (the distance is the enn metric: between cell centres).

With return_id, the id of the neighbour patch: one row per neighbour when several are equally near, found with landscapemetrics' own search (same ties). NaN / NA for classes with a single patch.

Returns:

Type Description
DataFrame

class, id, dist (and id_neighbour).

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> nn = lc.nearest_neighbours(return_id=True)
>>> nn.columns.tolist()
['class', 'id', 'dist', 'id_neighbour']

circumscribing_circles

circumscribing_circles(level: str = 'patch') -> DataFrame

Smallest circle around every patch (or every class), like landscapemetrics' get_circumscribingcircle: it encloses the corners of the cells in the first and last rows and columns, as for the circle metric (with convention="fragstats", every cell of the patch).

Returns:

Type Description
DataFrame

class, id (patch level), diameter, center_x, center_y (map coordinates).

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> circles = lc.circumscribing_circles()
>>> "diameter" in circles.columns
True

adjacencies

adjacencies(neighbourhood=4, what: str = 'full', upper: bool = False) -> DataFrame

Cell adjacencies between classes, like landscapemetrics' get_adjacencies: the number of (cell, neighbour) pairs, row = class of the cell, column = class of the neighbour (NA cells left out).

Parameters:

Name Type Description Default
neighbourhood

4, 8, or a matrix with 1 for the neighbours and 0 for the focal cell (read transposed, as landscapemetrics does).

4
what str

"full" (counts), or only "like" (diagonal), "unlike" (off-diagonal) or "triangle" (with the diagonal) of the lower triangle (upper with upper), NaN elsewhere.

'full'

Returns:

Type Description
DataFrame

Class x class counts (index: class of the cell, columns: class of the neighbour).

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> table = lc.adjacencies(4)
>>> table.shape == (len(lc.classes), len(lc.classes))
True

boundaries

boundaries(
    consider_boundary: bool = False,
    edge_depth: int = 1,
    as_na: bool = False,
    patch_id: bool = False,
)

Boundary cells of the patches.

1 for the cells within edge_depth cells (4 neighbours) of another class, of NA or, unless consider_boundary, of the map edge; 0 inside. The cells that are not boundary are the core cells of the core-area metrics.

Parameters:

Name Type Description Default
consider_boundary bool

Do not count the map edge as a boundary.

False
edge_depth int

Width of the boundary, in cells.

1
as_na bool

NaN instead of 0 for the inner cells.

False
patch_id bool

The patch id instead of 1 on boundary cells.

False

Returns:

Type Description
DataArray or ndarray

Same grid as the map, NaN where it is NA.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> edges = lc.boundaries(edge_depth=2)
>>> sorted(int(v) for v in set(edges.values[~edges.isnull().values].ravel()))
[0, 1]

extract_metrics

extract_metrics(
    y,
    *,
    extract_id=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    extras: bool = False,
    verbose: bool = True,
) -> DataFrame

Patch metrics of the patches under points; see extract_metrics().

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> xmin, xmax, ymin, ymax = lc.extent
>>> points = [[xmin + 1000, ymin + 1000], [xmax - 1000, ymax - 1000]]
>>> table = lc.extract_metrics(points, what=["lsm_p_area"])
>>> table["extract_id"].tolist()
[1, 2]

show_patches

show_patches(classes='global', **kwargs)

Draw the patches (needs matplotlib); see show_patches().

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> fig = lc.show_patches()

show_cores

show_cores(classes='all', **kwargs)

Draw the core and edge cells of every class (needs matplotlib); see show_cores().

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc"))[:300, :300])
>>> fig = lc.show_cores(classes=[12, 23])

show_lsm

show_lsm(metric: str, classes='global', **kwargs)

Draw a patch metric on the patches (needs matplotlib); see show_lsm().

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> fig = lc.show_lsm("area")

landscape_metrics

landscape_metrics(
    data,
    level=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    *,
    extras: bool = False,
    full_name: bool = False,
    n_threads: int | None = None,
    **options,
) -> DataFrame

Landscape metrics of one or several rasters, in long format.

The one-call equivalent of landscapemetrics' calculate_lsm.

Parameters:

Name Type Description Default
data

A raster (see Landscape), or a list of rasters / RasterCollection (a layer column numbers them from 1).

required
level

Metric selection, as in list_metrics(); all metrics by default.

None
metric

Metric selection, as in list_metrics(); all metrics by default.

None
name

Metric selection, as in list_metrics(); all metrics by default.

None
type

Metric selection, as in list_metrics(); all metrics by default.

None
what

Metric selection, as in list_metrics(); all metrics by default.

None
extras bool

Also select the FRAGSTATS metrics missing in landscapemetrics (_am, _md, _ra, ...; see list_metrics()).

False
full_name bool

Add the columns name, type and function_name (as calculate_lsm(full_name = TRUE)).

False
n_threads int | None

Threads of the C++ core (default: all cores but one).

None
**options

Landscape options (directions, count_boundary, edge_depth, res, nodata, block_rows for files larger than memory, ...).

{}

Returns:

Type Description
DataFrame

level, class, id, metric, value (plus layer for several rasters). Several rasters are computed in parallel.

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> table = ls.landscape_metrics(landcover, level="landscape", metric=["shdi", "contag"])
>>> table[["metric", "value"]].round(2)
   metric  value
0  contag  69.46
1    shdi   1.81

Several rasters (a layer column numbers them):

>>> both = ls.landscape_metrics([landcover, landcover[:1000]], what=["lsm_l_np"])
>>> both["layer"].tolist()
[1, 2]

list_metrics

list_metrics(
    level=None, metric=None, name=None, type=None, what=None, *, extras: bool = False
) -> DataFrame

Available landscape metrics.

Filters work as in landscapemetrics' list_lsm: level ("patch", "class", "landscape"), metric (family, e.g. "area" selects area, area_mn, area_sd and area_cv), name and type (e.g. "aggregation metric") are combined, and names starting with - exclude. what lists levels and/or function names such as "lsm_c_ca" and overrides the other filters.

extras=True adds the metrics landscapemetrics does not have (FRAGSTATS / pylandstats): the area-weighted mean _am, the median _md and the range _ra of every patch metric, the statistics of perim, and FRAGSTATS' proximity (prox), similarity (simi), connectance (connect) and edge-contrast (econ, cwed, teci) metrics. Extras named in what are always selected.

Parameters:

Name Type Description Default
level

"patch", "class" and/or "landscape".

None
metric

Metric families, e.g. "area" (area, area_mn...), with aliases ("shape_index").

None
name

Full names or types, e.g. "aggregation metric".

None
type

Full names or types, e.g. "aggregation metric".

None
what

Levels and function names such as "lsm_c_ca"; overrides the other filters.

None
extras bool

Include the FRAGSTATS metrics landscapemetrics does not have.

False

Returns:

Type Description
DataFrame

Columns metric, name, type, level, extra (not in landscapemetrics) and function_name.

Examples:

>>> import landschaft as ls
>>> len(ls.list_metrics()) > 130
True
>>> ls.list_metrics(level="landscape", type="diversity metric")["metric"].tolist()
['msidi', 'msiei', 'pr', 'prd', 'rpr', 'shdi', 'shei', 'sidi', 'siei']
>>> no_core = ls.list_metrics(level="class", type="-core area metric")

Plots, windows and zones

sample_metrics

sample_metrics(
    data,
    y,
    *,
    size: float | None = None,
    shape: str = "square",
    plot_id=None,
    all_classes: bool = False,
    level=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    res=None,
    nodata=None,
    band: int | None = None,
    verbose: bool = True,
    extras: bool = False,
    full_name: bool = False,
    return_raster: bool = False,
    n_threads: int | None = None,
    **options,
) -> DataFrame

Landscape metrics of sample plots, like landscapemetrics' sample_lsm.

Each plot is cut from the raster as terra::crop(mask = TRUE) does (grid window snapped to the nearest cell edges; every cell the plot touches is kept, the others become NA) and its metrics are computed as a landscape of its own. All plots are computed in parallel.

Parameters:

Name Type Description Default
data

A raster (see Landscape) or a list of rasters / RasterCollection (layer column).

required
y

Sample plots: an (n, 2) array of x/y coordinates or point geometries (buffers of shape and size around them), or polygons. Geometries can be shapely objects, a GeoSeries / GeoDataFrame (anything with __geo_interface__) or GeoJSON-like dicts, in the raster's coordinates. As in landscapemetrics, the plot area behind percentage_inside is geodesic when y has a CRS (y.crs, e.g. a GeoDataFrame) and planar otherwise.

required
size float | None

Buffer size around points: half the side of a square, or the radius of a circle (landscapemetrics' 100-vertex polygon).

None
shape str

"square" (default) or "circle".

'square'
plot_id

Identifiers of the plots (default 1..n).

None
all_classes bool

Add class-level rows (NA) for the classes of the whole raster that are missing in a plot (with a row for class NA when the raster has NA cells, as landscapemetrics does).

False
level

Metric selection, as in list_metrics().

None
metric

Metric selection, as in list_metrics().

None
name

Metric selection, as in list_metrics().

None
type

Metric selection, as in list_metrics().

None
what

Metric selection, as in list_metrics().

None
res

Raster reading options (see Landscape).

None
nodata

Raster reading options (see Landscape).

None
band

Raster reading options (see Landscape).

None
verbose bool

Warn when a plot lies less than 90 % inside the raster.

True
extras bool

Also select the FRAGSTATS metrics missing in landscapemetrics.

False
full_name bool

Add the columns name, type and function_name after value (landscapemetrics' sample_lsm(full_name = TRUE) drops function_name when return_raster = FALSE, by a fixed column index; it is kept here).

False
return_raster bool

Add a column raster_sample_plots with each plot's raster (an xarray.DataArray: the snapped window, NaN outside the plot and on NA cells; None for a plot outside the raster).

False
n_threads int | None

Threads of the C++ core (default: all cores but one).

None
**options

Metric options (directions, edge_depth, ...).

{}

Returns:

Type Description
DataFrame

level, class, id, metric, value, plot_id and percentage_inside (area of the plot's cells over the plot area, in %), ordered by plot, level, metric, class and id; plus layer for several rasters. A plot outside the raster (an error in landscapemetrics) gives NA metrics and 0 %.

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> import numpy as np
>>> xmin, xmax, ymin, ymax = ls.Landscape(landcover).extent
>>> points = np.array([[xmin + 5000, ymin + 5000], [xmax - 5000, ymax - 5000]])
>>> plots = ls.sample_metrics(landcover, points, size=1000, shape="circle",
...                           level="landscape", metric=["shdi", "np"])
>>> plots["plot_id"].unique().tolist()
[1, 2]

scale_sample

scale_sample(
    data,
    y,
    *,
    size,
    shape: str = "square",
    level=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    verbose: bool = True,
    full_name: bool = False,
    **options,
) -> DataFrame

Landscape metrics of buffers of several sizes around points, like landscapemetrics' scale_sample.

sample_metrics() for each buffer size in size (the plots of all sizes are computed in parallel). y: an (n, 2) array of coordinates or point geometries; other arguments (full_name too) as in sample_metrics().

Returns:

Type Description
DataFrame

level, class, id, metric, value, size, plot_id, percentage_inside (plus layer), ordered by (layer,) plot, level, metric, class, id and size.

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> import numpy as np
>>> xmin, xmax, ymin, ymax = ls.Landscape(landcover).extent
>>> points = np.array([[xmin + 5000, ymin + 5000], [xmax - 5000, ymax - 5000]])
>>> scales = ls.scale_sample(landcover, points, size=[500, 1000, 2000], what=["lsm_l_shdi"])
>>> sorted(scales["size"].unique().tolist())
[500.0, 1000.0, 2000.0]

extract_metrics

extract_metrics(
    data,
    y,
    *,
    extract_id=None,
    metric=None,
    name=None,
    type=None,
    what=None,
    extras: bool = False,
    full_name: bool = False,
    verbose: bool = True,
    n_threads: int | None = None,
    **options,
) -> DataFrame

Patch metrics of the patches under points, like landscapemetrics' extract_lsm.

Parameters:

Name Type Description Default
data

A raster (see Landscape) or a list of rasters / RasterCollection (layer column).

required
y

Points: an (n, 2) array of x/y coordinates or point geometries, in the raster's coordinates. A point on a cell edge belongs to the cell to the right / below (terra's rule); points outside the raster or on NA cells give no rows.

required
extract_id

Identifiers of the points (default 1..n).

None
metric

Selection of patch-level metrics, as in list_metrics() (all patch metrics by default).

None
name

Selection of patch-level metrics, as in list_metrics() (all patch metrics by default).

None
type

Selection of patch-level metrics, as in list_metrics() (all patch metrics by default).

None
what

Selection of patch-level metrics, as in list_metrics() (all patch metrics by default).

None
extras

Selection of patch-level metrics, as in list_metrics() (all patch metrics by default).

None
full_name bool

Add the columns name, type and function_name after value.

False
**options

Landscape options (directions, res, ...).

{}

Returns:

Type Description
DataFrame

level, class, id, metric, value, extract_id (plus layer), one row per point, patch and metric, ordered by (layer,) extract_id, level, metric, class and id.

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> import numpy as np
>>> xmin, xmax, ymin, ymax = ls.Landscape(landcover).extent
>>> points = np.array([[xmin + 5000, ymin + 5000], [xmax - 5000, ymax - 5000]])
>>> table = ls.extract_metrics(landcover, points, what=["lsm_p_area", "lsm_p_shape"])
>>> sorted(table["metric"].astype(str).unique())
['area', 'shape']

window_metrics

window_metrics(
    data,
    window,
    level="landscape",
    metric=None,
    name=None,
    type=None,
    what=None,
    *,
    res=None,
    nodata=None,
    band: int | None = None,
    extras: bool = False,
    n_threads: int | None = None,
    **options,
)

Landscape-level metrics in a moving window, like landscapemetrics' window_lsm.

For every cell, the cells of the window centred on it form a landscape (cells outside the raster or outside the window shape are NA), and its metrics become the cell's values. Windows are computed in parallel.

Parameters:

Name Type Description Default
data

A raster (see Landscape) or a list of rasters / RasterCollection.

required
window

Odd side of a square window, or a 2-D array with odd sides whose cells that are True / non-zero (and not NaN) form the window, e.g. a circle.

required
level

Metric selection, as in list_metrics(); only landscape-level metrics are possible (all of them by default).

'landscape'
metric

Metric selection, as in list_metrics(); only landscape-level metrics are possible (all of them by default).

'landscape'
name

Metric selection, as in list_metrics(); only landscape-level metrics are possible (all of them by default).

'landscape'
type

Metric selection, as in list_metrics(); only landscape-level metrics are possible (all of them by default).

'landscape'
what

Metric selection, as in list_metrics(); only landscape-level metrics are possible (all of them by default).

'landscape'
res

Raster reading options (see Landscape).

None
nodata

Raster reading options (see Landscape).

None
band

Raster reading options (see Landscape).

None
extras bool

Also select the FRAGSTATS metrics missing in landscapemetrics.

False
n_threads int | None

Threads of the C++ core (default: all cores but one).

None
**options

Metric options (directions, edge_depth, ...).

{}

Returns:

Type Description
dict

{metric: raster} (a DataArray on the input grid, or a numpy array), or a list of such dicts for several rasters.

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> maps = ls.window_metrics(landcover[:300, :300], window=21, what=["lsm_l_shdi", "lsm_l_ed"])
>>> sorted(maps)
['ed', 'shdi']

A circular window:

>>> import numpy as np
>>> disc = np.hypot(*np.mgrid[-5:6, -5:6]) <= 5
>>> contagion = ls.window_metrics(landcover[:300, :300], window=disc, metric="contag")

zonal_metrics

zonal_metrics(
    data,
    zones,
    level="landscape",
    metric=None,
    name=None,
    type=None,
    what=None,
    *,
    extras: bool = False,
    separator: str = "_",
    **options,
)

Landscape and class metrics of every zone, as a GeoDataFrame.

The zones are cut out of the raster as in sample_metrics() (same values), and the results come back in wide form next to the zones' geometry and attributes: one row per zone, one column per landscape-level metric and one per class-level metric and class (<metric>_<class>), plus percentage_inside. Write it with write_table() or .to_file("zones.gpkg").

Parameters:

Name Type Description Default
data

One raster (see Landscape).

required
zones

Polygons: a GeoDataFrame / GeoSeries (attributes and CRS are kept), shapely polygons or GeoJSON-like geometries, in the raster's coordinates.

required
level

Metric selection (class and landscape level), as in list_metrics(); landscape level by default.

'landscape'
metric

Metric selection (class and landscape level), as in list_metrics(); landscape level by default.

'landscape'
name

Metric selection (class and landscape level), as in list_metrics(); landscape level by default.

'landscape'
type

Metric selection (class and landscape level), as in list_metrics(); landscape level by default.

'landscape'
what

Metric selection (class and landscape level), as in list_metrics(); landscape level by default.

'landscape'
extras

Metric selection (class and landscape level), as in list_metrics(); landscape level by default.

'landscape'
separator str

Between metric and class in the class-level column names.

'_'
**options {}

Returns:

Type Description
GeoDataFrame

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> zones = ls.grid_zones(landcover, 10000)          # 10 km squares
>>> table = ls.zonal_metrics(landcover, zones, level="landscape", metric=["shdi"])
>>> len(table)
25
>>> _ = ls.write_table(table, "zones.gpkg")

grid_zones

grid_zones(data, size, *, res=None, nodata=None, band=None) -> list[dict]

Square zones tiling a raster, for zonal analyses.

Squares of side size from the top-left corner of the raster (partial squares at the right and bottom edges), in row-major order.

Parameters:

Name Type Description Default
data

The raster (path, DataArray or array).

required
size

Side of the squares, in map units.

required
res

Reading options, as in Landscape.

None
nodata

Reading options, as in Landscape.

None
band

Reading options, as in Landscape.

None

Returns:

Type Description
list of dict

GeoJSON-like polygons, accepted by sample_metrics(), zonal_metrics() and the analyses.

Examples:

>>> import landschaft as ls
>>> zones = ls.grid_zones(ls.example_data("clc"), 5000)
>>> len(zones), zones[0]["type"]
(100, 'Polygon')

Metric tables (and zonal metrics as GeoDataFrames) are written with ls.write_table or ls.save.

Time series and spatial signatures

SpatioTemporalAnalysis

SpatioTemporalAnalysis(
    rasters,
    dates=None,
    *,
    zones=None,
    points=None,
    size=None,
    shape: str = "square",
    grid=None,
    **options,
)

Landscape metrics along a time series of rasters (pylandstats' SpatioTemporalAnalysis and its zonal, buffer and grid variants).

Parameters:

Name Type Description Default
rasters

The rasters (paths, DataArrays, arrays) or a RasterCollection, one per date.

required
dates

Labels of the rasters (default 1..n).

None
zones

Polygons to analyse separately (as in sample_metrics()).

None
points

Buffers of size around points instead of zones.

None
size

Buffers of size around points instead of zones.

None
shape

Buffers of size around points instead of zones.

None
grid

Side of square zones tiling the rasters instead of zones.

None
**options

Metric options (directions, edge_depth, ...).

{}

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> import numpy as np
>>> later = landcover.copy()
>>> later.values[:500, :500] = np.where(later.values[:500, :500] == 12, 1, later.values[:500, :500])
>>> series = ls.SpatioTemporalAnalysis([landcover, later], dates=[2018, 2030])
>>> series.landscape_metrics(["np", "shdi"]).columns.tolist()     # indexed by date
['np', 'shdi']

metrics

metrics(
    level=None, metric=None, name=None, type=None, what=None, *, extras: bool = False
) -> DataFrame

Selected metrics of every date, in long format.

Parameters:

Name Type Description Default
level

Selection, as in list_metrics().

None
metric

Selection, as in list_metrics().

None
name

Selection, as in list_metrics().

None
type

Selection, as in list_metrics().

None
what

Selection, as in list_metrics().

None
extras

Selection, as in list_metrics().

None

Returns:

Type Description
DataFrame

The long table of landscape_metrics() with a date column (and zone and percentage_inside for zonal analyses).

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> series = ls.SpatioTemporalAnalysis([landcover, landcover[:1000]], dates=["full", "north"])
>>> series.metrics(what=["lsm_l_ta"])["date"].tolist()
['full', 'north']

landscape_metrics

landscape_metrics(metrics=None) -> DataFrame

Landscape-level metrics, one row per date (and zone).

Parameters:

Name Type Description Default
metrics

Metric names (default: all landscape metrics).

None

Returns:

Type Description
DataFrame

One column per metric, indexed by date (and zone).

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> series = ls.SpatioTemporalAnalysis([landcover, landcover[:1000]], dates=[1, 2])
>>> series.landscape_metrics(["ta"])["ta"].tolist()
[250000.0, 125000.0]

class_metrics

class_metrics(metrics=None, classes=None, *, fillna: bool = True) -> DataFrame

Class-level metrics, one row per class and date (and zone).

Parameters:

Name Type Description Default
metrics

Metric names (default: all class metrics).

None
classes

Class values to keep (default: all).

None
fillna bool

A class absent at a date gets 0 for area, edge and count metrics (ca, pland, np, te...) instead of NaN.

True

Returns:

Type Description
DataFrame

One column per metric, indexed by class and date (and zone).

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> series = ls.SpatioTemporalAnalysis([landcover, landcover[:1000]], dates=[1, 2])
>>> table = series.class_metrics(["pland"], classes=[23, 24])
>>> table.index.names
FrozenList(['class', 'date'])

plot_metric

plot_metric(metric: str, classes=None, *, ax=None, **plot_kwargs)

Line plot of a metric over the dates (needs matplotlib).

Parameters:

Name Type Description Default
metric str

A landscape or class metric.

required
classes

None (landscape level), "all" or class values: one line per class.

None
ax

A matplotlib axes to draw in.

None
**plot_kwargs

Passed to matplotlib's plot.

{}

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> series = ls.SpatioTemporalAnalysis([landcover, landcover[:1000]], dates=[2000, 2020])
>>> fig = series.plot_metric("pland", classes=[12, 23])

SpatialSignatureAnalysis

SpatialSignatureAnalysis(
    rasters=None,
    *,
    landscape_metrics=None,
    class_metrics=None,
    classes=None,
    fillna: bool = True,
    zones=None,
    points=None,
    size=None,
    shape: str = "square",
    grid=None,
    table: DataFrame = None,
    **options,
)

Landscapes (or zones) described by their metrics: the "spatial signature" matrix, its principal components and clusters (pylandstats' SpatialSignatureAnalysis).

Parameters:

Name Type Description Default
rasters

Rasters (or a RasterCollection) to compare, or one raster with zones / points + size / grid.

None
landscape_metrics

Metric names; class metrics become one column per class (<metric>_<class>), restricted to classes if given.

None
class_metrics

Metric names; class metrics become one column per class (<metric>_<class>), restricted to classes if given.

None
fillna bool

0 instead of NaN for area / edge / count class metrics of absent classes.

True
zones

Zonal signatures of one raster (see SpatioTemporalAnalysis).

None
points

Zonal signatures of one raster (see SpatioTemporalAnalysis).

None
size

Zonal signatures of one raster (see SpatioTemporalAnalysis).

None
shape

Zonal signatures of one raster (see SpatioTemporalAnalysis).

None
grid

Zonal signatures of one raster (see SpatioTemporalAnalysis).

None
**options

Metric options.

{}

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> signatures = ls.SpatialSignatureAnalysis(landcover, grid=5000, landscape_metrics=["shdi", "ed", "np"],
...                                          class_metrics=["pland"])
>>> signatures.table.shape
(100, 29)
>>> groups = signatures.cluster(4)

from_table classmethod

from_table(table: DataFrame) -> SpatialSignatureAnalysis

An analysis of a ready signature matrix.

Parameters:

Name Type Description Default
table DataFrame

Rows = landscapes or zones, columns = metrics.

required

Returns:

Type Description
SpatialSignatureAnalysis

Examples:

>>> import pandas as pd
>>> import landschaft as ls
>>> table = pd.DataFrame({"shdi": [1.2, 0.8, 1.5], "ed": [40.0, 22.0, 55.0]})
>>> ls.SpatialSignatureAnalysis.from_table(table).table.shape
(3, 2)

pca

pca(n_components: int | None = None) -> PCAResult

Principal components of the standardised metrics.

The metrics are turned into z-scores; constant metrics are left out. Signs as scikit-learn's PCA (the largest loading of each component is positive).

Parameters:

Name Type Description Default
n_components int | None

Components to keep (default: all).

None

Returns:

Type Description
PCAResult

Scores, loadings and explained variance ratios.

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> pca = signatures.pca(2)
>>> pca.scores.shape
(100, 2)

nmf

nmf(n_components: int, *, max_iter: int = 1000, tol: float = 1e-06) -> NMFResult

Non-negative matrix factorisation of the metrics.

Each metric is scaled to [0, 1] (constant metrics left out); scores (W) times components (H) approximates them, both non-negative, with the Frobenius loss (NNDSVDa start, HALS updates, as scikit-learn's NMF(init="nndsvda", solver="cd")). Deterministic.

Parameters:

Name Type Description Default
n_components int

Number of factors.

required
max_iter int

Maximum number of sweeps.

1000
tol float

Stop when the loss improves by less than this (relative).

1e-06

Returns:

Type Description
NMFResult

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> factors = signatures.nmf(2)
>>> factors.components.shape
(4, 2)

cluster

cluster(
    n_clusters: int,
    *,
    method: str = "kmeans",
    n_components: int | None = None,
    n_init: int = 10,
    seed: int = 0,
) -> Series

Groups of landscapes (or zones) with similar metrics.

Parameters:

Name Type Description Default
n_clusters int

Number of groups.

required
method str

"kmeans" (k-means++ starts, the best of n_init) or a hierarchical linkage: "ward", "average", "complete", "single", "centroid", "median", "weighted".

'kmeans'
n_components int | None

Cluster the first principal components instead of all metrics.

None
n_init int

k-means restarts and random seed.

10
seed int

k-means restarts and random seed.

10

Returns:

Type Description
Series

The group of every row, numbered 0..k-1 in order of first appearance.

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> groups = signatures.cluster(3)
>>> sorted(groups.unique().tolist())
[0, 1, 2]
>>> ward = signatures.cluster(3, method="ward")

clustergram

clustergram(
    k_range=range(1, 9),
    *,
    method: str = "kmeans",
    n_components: int | None = None,
    pca_weighted: bool = True,
    n_init: int = 10,
    seed: int = 0,
) -> ClustergramResult

Clusters for a range of k, for a clustergram.

For every k in k_range, the cluster of every row and the mean of its cluster (the centre projected on the first principal axis, or averaged over the columns).

Parameters:

Name Type Description Default
k_range

Numbers of clusters.

range(1, 9)
method str

As in cluster().

'kmeans'
n_components str

As in cluster().

'kmeans'
n_init str

As in cluster().

'kmeans'
seed str

As in cluster().

'kmeans'
pca_weighted bool

Project the cluster means on the first principal axis.

True

Returns:

Type Description
ClustergramResult

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> cg = signatures.clustergram(range(1, 5))
>>> cg.labels.shape
(100, 4)

plot_clustergram

plot_clustergram(
    result: ClustergramResult | None = None,
    *,
    ax=None,
    size: float = 1.0,
    linewidth: float = 1.0,
    **kwargs,
)

Draw a clustergram (needs matplotlib).

For every k, a dot per cluster at its mean, sized by its rows, and lines between the clusters of k and k + 1 as wide as the rows they share.

Parameters:

Name Type Description Default
result ClustergramResult | None

A clustergram() result (computed with kwargs when not given).

None
ax

A matplotlib axes to draw in.

None
size float

Scale of the dots and lines.

1.0
linewidth float

Scale of the dots and lines.

1.0
**kwargs

Passed to clustergram().

{}

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> fig = signatures.plot_clustergram(k_range=range(1, 6))

plot_pca

plot_pca(
    clusters: Series | None = None, components=(1, 2), *, ax=None, labels: bool = False
)

Scatter plot of two principal components, with the metric loadings as arrows.

Parameters:

Name Type Description Default
clusters Series | None

Colour the points by these groups (e.g. from cluster()).

None
components

The two components to draw (1-based).

(1, 2)
ax

A matplotlib axes to draw in.

None
labels bool

Write the row labels.

False

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> fig = signatures.plot_pca(signatures.cluster(3))

plot_cluster_metrics

plot_cluster_metrics(clusters: Series)

Box plots of every metric per cluster.

Parameters:

Name Type Description Default
clusters Series

Groups of the rows, e.g. from cluster().

required

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> signatures = ls.SpatialSignatureAnalysis(ls.example_data("clc"), grid=5000,
...                                          landscape_metrics=["shdi", "ed", "np", "lpi"])
>>> fig = signatures.plot_cluster_metrics(signatures.cluster(3))

PCAResult dataclass

PCAResult(
    scores: DataFrame,
    loadings: DataFrame,
    explained_variance_ratio: ndarray,
    dropped: list,
)

Bases: Saveable

Principal components of the spatial signatures.

NMFResult dataclass

NMFResult(
    scores: DataFrame,
    components: DataFrame,
    reconstruction_error: float,
    n_iter: int,
    dropped: list,
)

Bases: Saveable

Non-negative factors of the spatial signatures (min-max scaled metrics ~ scores @ components.T).

ClustergramResult dataclass

ClustergramResult(
    labels: DataFrame, means: DataFrame, pca_weighted: bool, dropped: list
)

Bases: Saveable

Clusters for a range of k (Schonlau's clustergram, as the clustergram package that pylandstats' get_cgram uses).

Plots

show_patches

show_patches(
    landscape,
    classes="global",
    *,
    labels: bool = False,
    cmap="tab20",
    ncols=None,
    nrows=None,
    **options,
)

Map of the patches, each in its own colour (landscapemetrics' show_patches).

Parameters:

Name Type Description Default
landscape

A Landscape, or a raster (then **options are the Landscape options, e.g. directions).

required
classes

"global" (all patches on one map), "all" (one map per class) or class value(s).

'global'
labels bool

Write the patch ids at the patch centroids.

False
cmap

Colour map (cycled over patch ids) and panel layout.

'tab20'
ncols

Colour map (cycled over patch ids) and panel layout.

'tab20'
nrows

Colour map (cycled over patch ids) and panel layout.

'tab20'

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))[:400, :400]
>>> fig = ls.show_patches(landcover)
>>> fig = ls.show_patches(landcover, classes=[12, 23], labels=True)

show_cores

show_cores(
    landscape,
    classes="all",
    *,
    edge_depth: int = 1,
    consider_boundary: bool = True,
    labels: bool = False,
    ncols=None,
    nrows=None,
    **options,
)

Core and edge cells of the patches (landscapemetrics' show_cores): core cells dark, edge cells light, other classes blank.

edge_depth and consider_boundary as for the core-area metrics (landscapemetrics' plot defaults: consider_boundary=True); labels writes the core area (ha) of each patch.

Parameters:

Name Type Description Default
landscape

A Landscape, or a raster (**options are then the Landscape options).

required
classes

"all" (one panel per class, default), "global" or class value(s).

'all'
edge_depth int

As for the core-area metrics.

1
consider_boundary int

As for the core-area metrics.

1
labels bool

Write the core area (ha) of each patch.

False
ncols

Panel layout.

None
nrows

Panel layout.

None

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))[:400, :400]
>>> fig = ls.show_cores(landcover, classes=[23], edge_depth=2)

show_lsm

show_lsm(
    landscape,
    metric: str,
    classes="global",
    *,
    labels: bool = False,
    cmap="viridis",
    ncols=None,
    nrows=None,
    **options,
)

A patch-level metric painted on the patches (landscapemetrics' show_lsm), with a colour bar; labels writes the values.

Parameters:

Name Type Description Default
landscape

A Landscape, or a raster (**options are then the Landscape options).

required
metric str

A patch-level metric.

required
classes

"global" (one map, default), "all" or class value(s).

'global'
labels bool

Write the values on the patches.

False
cmap

Colour map and panel layout.

'viridis'
ncols

Colour map and panel layout.

'viridis'
nrows

Colour map and panel layout.

'viridis'

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))[:400, :400]
>>> fig = ls.show_lsm(landcover, "shape")

show_correlation

show_correlation(
    data,
    method: str = "pearson",
    *,
    diag: bool = True,
    labels: bool = False,
    cmap="RdBu_r",
)

Heatmap of the correlations between metrics (landscapemetrics' show_correlation), one panel per level.

Parameters:

Name Type Description Default
data

A long table of metrics (correlated with calculate_correlation() and method) or its result (a dict of tables, or one table).

required
labels bool

Write the correlation values in the cells.

False

Returns:

Type Description
Figure

Examples:

>>> import landschaft as ls
>>> table = ls.landscape_metrics(ls.example_data("clc"), what=["lsm_p_area", "lsm_p_perim", "lsm_p_shape"])
>>> fig = ls.show_correlation(table, method="spearman", labels=True)

Utilities

check_landscape

check_landscape(
    data, verbose: bool = True, *, nodata=None, band: int | None = None
) -> DataFrame

Is a raster suitable for landscape metrics? Like landscapemetrics' check_landscape.

Parameters:

Name Type Description Default
data

A raster or a list of rasters.

required
verbose bool

Warn about the problems found.

True
nodata

Reading options, as in Landscape.

None
band

Reading options, as in Landscape.

None

Returns:

Type Description
DataFrame

One row per raster: layer, crs ("projected", "geographic" or NA), units (of the CRS, e.g. "m"), class ("integer", "non-integer", or NA when all values are NA), n_classes and OK: ✔ (integer classes, metric CRS, at most 30 classes), ✖ (non-integer values or degrees) or ❓ (no CRS, or more than 30 classes). verbose warns about the problems. Like terra, a raster file without CRS whose extent fits longitudes and latitudes is taken as geographic.

Examples:

>>> import landschaft as ls
>>> check = ls.check_landscape(ls.example_data("clc"), verbose=False)
>>> check[["crs", "units", "class", "n_classes"]]
        crs units    class  n_classes
0  projected     m  integer         26

calculate_correlation

calculate_correlation(
    metrics: DataFrame,
    method: str = "pearson",
    diag: bool = True,
    simplify: bool = False,
)

Correlation between metrics, like landscapemetrics' calculate_correlation.

Parameters:

Name Type Description Default
metrics DataFrame

A long table of metrics (Landscape.metrics, landscape_metrics, sample_metrics...). Patch-level metrics are correlated over patch ids, class-level metrics over classes, landscape-level metrics over layers (layer column; needs several rasters). As in R's xtabs, values of the same id (class, layer) are summed, missing combinations count as 0, NA values give NA correlations for their metric (whose diagonal stays 1) and rows without id / class are left out.

required
method str

"pearson", "spearman" or "kendall" (R's cor, same values).

'pearson'
diag bool

Keep the diagonal (correlation 1).

True
simplify bool

Return the table itself when a single level is present.

False

Returns:

Type Description
dict or DataFrame

{level: DataFrame(metric_1, metric_2, value)} with the lower triangle of the correlation matrix (column by column, as R).

Examples:

>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> table = ls.landscape_metrics(landcover, what=["lsm_p_area", "lsm_p_perim", "lsm_p_shape"])
>>> corr = ls.calculate_correlation(table, method="spearman")
>>> sorted(corr)
['patch']

raster_boundaries

raster_boundaries(
    data,
    *,
    consider_boundary: bool = False,
    edge_depth: int = 1,
    as_na: bool = False,
    patch_id: bool = False,
    nodata=None,
    band: int | None = None,
    n_threads: int | None = None,
)

landscapemetrics' get_boundaries on the values of a raster.

The values are taken as they are (classes, or patch ids such as labels() or patches()). A cell is a boundary cell as in landscapemetrics' rcpp_get_boundaries: the cell below holds another value or NA, or the cell above, left or right is NA; the map edge counts as NA unless consider_boundary. As in landscapemetrics, patch_id is ignored (with a warning) when a value is 0. (On a map of several classes this marks only one side of a class change; boundaries() gives the boundaries of the patches of every class.) edge_depth > 1 peels the boundary off and repeats.

Parameters:

Name Type Description Default
data

A raster of classes or patch ids.

required
consider_boundary bool

Do not count the map edge as NA.

False
edge_depth int

Width of the boundary, in cells.

1
as_na bool

NaN instead of 0 for the inner cells.

False
patch_id bool

The cell value instead of 1 on boundary cells.

False
nodata

Reading options.

None
band

Reading options.

None
n_threads int | None

Threads (None / 0: all cores but one).

None

Returns:

Type Description
DataArray or ndarray

1 on boundary cells (their value with patch_id), 0 inside (NA with as_na), NaN where the raster is NA.

Examples:

>>> import landschaft as ls
>>> lc = ls.Landscape(ls.load_raster(ls.example_data("clc")))
>>> edges = ls.raster_boundaries(lc.labels(), edge_depth=1)

construct_buffer

construct_buffer(coords, shape: str, size: float, *, return_vec: bool = True, crs=None)

Square or circular sample plots around points, like landscapemetrics' construct_buffer.

Parameters:

Name Type Description Default
coords

Points: an (n, 2) array of x/y coordinates or point geometries.

required
shape str

"square" (half side size) or "circle" (radius size, 100 vertices).

required
size float

Buffer size, in map units.

required
return_vec bool

True: a geopandas.GeoDataFrame of polygons with plot_id 1..n. False: landscapemetrics' vertex matrix, an (n * k, 3) array of x, y and the plot number in the same row order (circles plot by plot, squares corner by corner), open rings.

True
crs

CRS of the polygons (return_vec only; default: the CRS of coords if it has one).

None
Notes

These are the plots sample_metrics() builds around points.

Examples:

>>> import landschaft as ls
>>> ls.construct_buffer([[0.0, 0.0]], "square", 10, return_vec=False)
array([[-10., -10.,   1.],
       [-10.,  10.,   1.],
       [ 10.,  10.,   1.],
       [ 10., -10.,   1.]])
>>> plots = ls.construct_buffer([[0.0, 0.0], [100.0, 0.0]], "circle", 25)   # GeoDataFrame
>>> len(plots)
2

pad_raster

pad_raster(
    data,
    pad_raster_value: float = -999,
    pad_raster_cells: int = 1,
    global_: bool = False,
    return_raster: bool = True,
    *,
    nodata=None,
    band: int | None = None,
)

Add rows and columns of pad_raster_value around a raster, like landscapemetrics' pad_raster.

Parameters:

Name Type Description Default
data

A raster (file, DataArray, numpy or masked array) or a list of them.

required
pad_raster_value float

Value of the new cells.

-999
pad_raster_cells int

Number of rows / columns added on each side.

1
global_ bool

Also replace the NA cells by pad_raster_value (R's global).

False
return_raster bool

Return a georeferenced raster (DataArray, NaN = NA, extent grown by the new cells) for georeferenced inputs; numpy input always gives a numpy array.

True
nodata

Reading options, as in Landscape.

None
band

Reading options, as in Landscape.

None

Returns:

Type Description
xarray.DataArray or numpy.ndarray (float), or a list for several rasters.

Examples:

>>> import numpy as np
>>> import landschaft as ls
>>> ls.pad_raster(np.array([[1, 2], [3, 4]]), pad_raster_value=0)
array([[0., 0., 0., 0.],
       [0., 1., 2., 0.],
       [0., 3., 4., 0.],
       [0., 0., 0., 0.]])

unpad_raster

unpad_raster(
    data,
    unpad_raster_cells: int = 1,
    return_raster: bool = True,
    *,
    nodata=None,
    band: int | None = None,
)

Remove unpad_raster_cells rows and columns on each side of a raster, like landscapemetrics' unpad_raster (the inverse of pad_raster()). Arguments and result as in pad_raster().

Parameters:

Name Type Description Default
data

A raster or a list of rasters.

required
unpad_raster_cells int

Rows and columns removed on each side.

1
return_raster bool

Return a georeferenced DataArray for georeferenced input.

True
nodata

Reading options.

None
band

Reading options.

None

Returns:

Type Description
xarray.DataArray or numpy.ndarray (float), or a list for several rasters.

Examples:

>>> import landschaft as ls
>>> padded = ls.pad_raster(ls.example_data("clc"), pad_raster_cells=2)
>>> ls.unpad_raster(padded, 2).shape
(2000, 2000)

raster_to_points

raster_to_points(
    data, return_NA: bool = True, *, nodata=None, band: int | None = None
) -> DataFrame

Cell centres and values, like landscapemetrics' raster_to_points.

Parameters:

Name Type Description Default
data

A raster or a list of rasters (see pad_raster()). Numpy input has terra's extent of a matrix (0..ncol, 0..nrow).

required
return_NA bool

Keep the NA cells (value NaN).

True

Returns:

Type Description
DataFrame

layer, x, y, z, cell by cell in row order (the coordinates are terra's xyFromCell).

Examples:

>>> import numpy as np
>>> import landschaft as ls
>>> ls.raster_to_points(np.array([[1, 2], [np.nan, 4]]), return_NA=False)
   layer    x    y    z
0      1  0.5  1.5  1.0
1      1  1.5  1.5  2.0
2      1  1.5  0.5  4.0

get_unique_values

get_unique_values(
    data, simplify: bool = False, *, nodata=None, band: int | None = None
)

The distinct values of rasters (NA left out, doubles truncated to integers), sorted, like landscapemetrics' get_unique_values.

data: a raster (file, DataArray, numpy array of any dimension up to 2, masked array) or a list of them. Returns {"layer_1": values, ...} as landscapemetrics does, or only the values of a single raster with simplify=True.

Parameters:

Name Type Description Default
data

A raster (file, DataArray, numpy or masked array, also 1-D) or a list of them.

required
simplify bool

Return the values of a single raster directly.

False
nodata

Reading options.

None
band

Reading options.

None

Returns:

Type Description
dict or ndarray

Examples:

>>> import landschaft as ls
>>> ls.get_unique_values(ls.example_data("input"), simplify=True).tolist()
[0, 1, 2]