Skip to content

Large data & batch

Many rasters at once, maps larger than memory, and tiled MSPA. Tutorial: large data and batch processing.

Most tools also accept a RasterCollection or a lazy stack directly (e.g. ls.mspa(ls.load_raster("tiles/*.tif"), output_dir="out")), and Landscape reads large maps in blocks (block_rows).

Folders of rasters

batch

Parallel processing of many rasters, one thread per raster.

batch() runs any landschaft tool on many rasters (GuidosToolbox's File -> Batch Process); mspa_batch() is the MSPA-specific version with GTB's output naming.

BatchResult dataclass

BatchResult(
    input: Path,
    output: Path | None,
    seconds: float,
    error: str | None = None,
    outputs: list[Path] = list(),
)

Bases: Saveable

Outcome of one input of batch() / mspa_batch().

Attributes:

Name Type Description
input Path

The input raster.

output Path | None

The main output file (None on error).

seconds float

Run time.

error str | None

The error message, or None.

outputs list[Path]

Every file written.

ok property

ok: bool

Whether the input was processed without error.

parse_memory

parse_memory(value: int | str | None) -> int | None

16_000_000_000, "16GB", "500 MiB" -> bytes.

run_parallel

run_parallel(
    items: list[T],
    work: Callable[[T], object],
    *,
    pixels: Callable[[T], int],
    n_workers: int | None,
    threads_per_worker: int,
    max_memory: int | str | None,
) -> list

Run work on every item with a thread pool and a memory budget.

Results come back in input order. work must not raise if the caller wants every item processed.

mspa_batch

mspa_batch(
    inputs,
    output_dir: PathLike,
    *,
    connectivity: int = 8,
    edge_width: int = 1,
    transition: bool = True,
    internal: bool = True,
    n_workers: int | None = None,
    threads_per_worker: int = 1,
    max_memory: int | str | None = None,
    overwrite: bool = False,
    raise_errors: bool = False,
    on=None,
) -> list[BatchResult]

Run MSPA on many rasters in parallel and write the results.

Each worker thread loads one raster, runs MSPA and writes <output_dir>/<name>_<connectivity>_<edge_width>_<transition>_<internal>.tif (GuidosToolbox naming), then moves to the next one. The C++ core releases the GIL, so the threads run fully in parallel, and memory stays bounded by the rasters being processed at the same time.

Parameters:

Name Type Description Default
inputs

A RasterCollection, a directory, a glob pattern or a list of raster paths (single band, coded 0/1/2).

required
output_dir PathLike

Directory for the results (created if needed).

required
connectivity int

MSPA parameters, see mspa().

8
edge_width int

MSPA parameters, see mspa().

8
transition int

MSPA parameters, see mspa().

8
internal int

MSPA parameters, see mspa().

8
n_workers int | None

Rasters processed concurrently. Default: all cores but one, divided by threads_per_worker.

None
threads_per_worker int

Threads inside each raster (default 1: best throughput for many rasters; raise it when processing a few very large ones).

1
max_memory int | str | None

Memory budget, e.g. "16GB". Workers wait until the raster they take fits (about 32 bytes per pixel). None: no limit.

None
overwrite bool

Recompute outputs that already exist.

False
raise_errors bool

Raise the first error instead of recording it in the results.

False
on

ls.helpers(): process the rasters on this computer and on the helpers of its group (see parallel), each with threads_per_worker threads (n_workers and max_memory do not apply).

None

Returns:

Type Description
list of BatchResult

One entry per input, in input order.

Examples:

>>> from pathlib import Path
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> Path("tiles").mkdir(exist_ok=True)
>>> for row in (0, 500):
...     _ = ls.write_raster(forest[row:row + 500, :500], f"tiles/forest_{row}.tif")
>>> runs = ls.mspa_batch("tiles", "mspa_out", edge_width=1)
>>> [run.output.name for run in runs]
['forest_0_8_1_1_1.tif', 'forest_500_8_1_1_1.tif']

mspa_collection

mspa_collection(
    collection: RasterCollection,
    *,
    output_dir: PathLike | None = None,
    n_workers: int | None = None,
    threads_per_worker: int = 1,
    max_memory: int | str | None = None,
    overwrite: bool = False,
    **params,
)

batch

batch(
    tool,
    inputs,
    output_dir: PathLike,
    *,
    n_workers: int | None = None,
    threads_per_worker: int = 1,
    max_memory: int | str | None = None,
    overwrite: bool = False,
    raise_errors: bool = False,
    table_format: str = "csv",
    on=None,
    **params,
) -> list[BatchResult]

Run a landschaft tool on many rasters in parallel and write its outputs.

GuidosToolbox's Batch Process for every tool: each worker thread loads one raster, runs tool(data, **params) and writes what it returns to output_dir, named after the input and the tool:

  • maps: <name>_<tool>[_<field>].tif (GeoTIFF with the input's georeferencing and, for GTB products, GTB's colour table);
  • tables: <name>_<tool>_<field>.csv (.parquet with table_format="parquet"; "landscape_metrics" writes its long table as <name>_landscape_metrics.csv); the text report (summary()): <name>_<tool>.txt; GTB's CSV report when the tool has one: <name>_<tool>_gtb.csv;
  • <name>_<tool>.json: tool, parameters, output files, scalar results and run time. An input whose JSON exists is skipped unless overwrite.

MSPA is written with GTB's naming (see mspa_batch()).

Parameters:

Name Type Description Default
tool

A name from BATCH_TOOLS (e.g. "accounting", "spa", "fad_multiscale") or any function taking a raster as first argument.

required
inputs

A RasterCollection, a directory, a glob pattern or a list of raster paths.

required
output_dir PathLike

Directory for the results (created if needed).

required
n_workers int | None

As in mspa_batch(). threads_per_worker is passed to the tool's n_threads/threads argument.

None
threads_per_worker int | None

As in mspa_batch(). threads_per_worker is passed to the tool's n_threads/threads argument.

None
max_memory int | None

As in mspa_batch(). threads_per_worker is passed to the tool's n_threads/threads argument.

None
overwrite int | None

As in mspa_batch(). threads_per_worker is passed to the tool's n_threads/threads argument.

None
raise_errors int | None

As in mspa_batch(). threads_per_worker is passed to the tool's n_threads/threads argument.

None
table_format str

"csv" (default) or "parquet" (needs pyarrow) for the tables.

'csv'
on

ls.helpers(): process the rasters on this computer and on the helpers of its group (see parallel), with the same outputs; tool is then a name from BATCH_TOOLS and params JSON values (numbers, strings, lists, dicts).

None
**params

Parameters of the tool, e.g. variant=6 for "spa".

{}

Returns:

Type Description
list of BatchResult

One entry per input, in input order; outputs lists every file written.

Examples:

Accounting of every raster of a folder:

>>> from pathlib import Path
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> Path("tiles").mkdir(exist_ok=True)
>>> for row in (0, 500):
...     _ = ls.write_raster(forest[row:row + 500, :500], f"tiles/forest_{row}.tif")
>>> runs = ls.batch("accounting", "tiles", "accounting_out")
>>> [run.ok for run in runs]
[True, True]
>>> [path.name for path in runs[0].outputs]
['forest_0_accounting_classes.tif', 'forest_0_accounting_ids.tif',
 'forest_0_accounting_area.tif', 'forest_0_accounting_stats.csv',
 'forest_0_accounting.txt', 'forest_0_accounting.json']

mspa_batch

mspa_batch(
    inputs,
    output_dir: PathLike,
    *,
    connectivity: int = 8,
    edge_width: int = 1,
    transition: bool = True,
    internal: bool = True,
    n_workers: int | None = None,
    threads_per_worker: int = 1,
    max_memory: int | str | None = None,
    overwrite: bool = False,
    raise_errors: bool = False,
    on=None,
) -> list[BatchResult]

Run MSPA on many rasters in parallel and write the results.

Each worker thread loads one raster, runs MSPA and writes <output_dir>/<name>_<connectivity>_<edge_width>_<transition>_<internal>.tif (GuidosToolbox naming), then moves to the next one. The C++ core releases the GIL, so the threads run fully in parallel, and memory stays bounded by the rasters being processed at the same time.

Parameters:

Name Type Description Default
inputs

A RasterCollection, a directory, a glob pattern or a list of raster paths (single band, coded 0/1/2).

required
output_dir PathLike

Directory for the results (created if needed).

required
connectivity int

MSPA parameters, see mspa().

8
edge_width int

MSPA parameters, see mspa().

8
transition int

MSPA parameters, see mspa().

8
internal int

MSPA parameters, see mspa().

8
n_workers int | None

Rasters processed concurrently. Default: all cores but one, divided by threads_per_worker.

None
threads_per_worker int

Threads inside each raster (default 1: best throughput for many rasters; raise it when processing a few very large ones).

1
max_memory int | str | None

Memory budget, e.g. "16GB". Workers wait until the raster they take fits (about 32 bytes per pixel). None: no limit.

None
overwrite bool

Recompute outputs that already exist.

False
raise_errors bool

Raise the first error instead of recording it in the results.

False
on

ls.helpers(): process the rasters on this computer and on the helpers of its group (see parallel), each with threads_per_worker threads (n_workers and max_memory do not apply).

None

Returns:

Type Description
list of BatchResult

One entry per input, in input order.

Examples:

>>> from pathlib import Path
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> Path("tiles").mkdir(exist_ok=True)
>>> for row in (0, 500):
...     _ = ls.write_raster(forest[row:row + 500, :500], f"tiles/forest_{row}.tif")
>>> runs = ls.mspa_batch("tiles", "mspa_out", edge_width=1)
>>> [run.output.name for run in runs]
['forest_0_8_1_1_1.tif', 'forest_500_8_1_1_1.tif']

BatchResult dataclass

BatchResult(
    input: Path,
    output: Path | None,
    seconds: float,
    error: str | None = None,
    outputs: list[Path] = list(),
)

Bases: Saveable

Outcome of one input of batch() / mspa_batch().

Attributes:

Name Type Description
input Path

The input raster.

output Path | None

The main output file (None on error).

seconds float

Run time.

error str | None

The error message, or None.

outputs list[Path]

Every file written.

ok property

ok: bool

Whether the input was processed without error.

Tools of batch

ls.BATCH_TOOLS lists the names batch accepts; any function taking a raster as its first argument works too.

accounting, clustering_fac, conefor_inputs, contagion_pff, contortion, distance_to_resistance, euclidean_distance, fad, fad_multiscale, fos, gray_spatial_convolution, gtn_components, gtn_importance, influence_zones, landscape_mosaic, landscape_mosaic_heatmap, landscape_mosaic_stats, mspa, parcellation, proximity, reclassify, restoration_status, spa, spatial_entropy, spatial_entropy_map, landscape_metrics, window_metrics.

Several computers

on=ls.helpers() runs the tasks of batch, mspa_batch, simulate.scenarios, simulate.calibrate, sdm.fit (its cross-validation), uncertainty.monte_carlo, the trade-off calibrations and replacement importance of ls.prioritize, parallel.map and parallel.tiled on this computer and on the computers of its group running landschaft worker. Tutorial: several computers.

helpers

helpers(
    *,
    local: bool = True,
    local_threads: int | None = None,
    port: int = TCP_PORT,
    discovery: bool = True,
    verbose: bool = True,
) -> Helpers

This computer and the helpers of its group, for on= (see Helpers).

Parameters:

Name Type Description Default
local bool

Run tasks on this computer too (False: only coordinate).

True
local_threads int | None

Cores of this computer for the tasks (default: all but one).

None
port int

TCP port helpers connect to.

TCP_PORT
discovery bool

Answer helpers that look for work on the local network.

True
verbose bool

Print who joined, the progress and failures.

True

Returns:

Type Description
Helpers

Examples:

>>> import landschaft as ls
>>> with ls.helpers() as pool:
...     runs = ls.batch("accounting", "tiles/", "out/", on=pool)

Helpers

Helpers(
    *,
    local: bool = True,
    local_threads: int | None = None,
    port: int = TCP_PORT,
    discovery: bool = True,
    verbose: bool = True,
    out: Callable[[str], None] | None = None,
    host: str = "",
)

This computer and the helpers of its group, for the on= argument of landschaft's functions that run many independent tasks.

Give it to batch(), mspa_batch(), scenarios() or calibrate() (grid and random searches): their tasks run here and on the computers of this computer's group running landschaft worker, with the same results as on this computer alone. Used in a with block, the helpers stay connected from one call to the next; otherwise each call starts and stops the coordinator.

Parameters:

Name Type Description Default
local bool

Run tasks on this computer too. local=False: this computer only hands out tasks and collects their outputs.

True
local_threads int | None

Cores of this computer for the tasks (default: all but one).

None
port int

TCP port helpers connect to (the discovery broadcasts use UDP 47810); 0 picks a free one.

TCP_PORT
discovery bool

Answer the broadcasts of helpers looking for work on the local network. Without it, helpers connect with landschaft worker --connect <this computer>.

True
verbose bool

Print who joined, the progress and failures.

True

Examples:

>>> import landschaft as ls
>>> with ls.helpers() as pool:
...     runs = ls.batch("fad", "tiles/", "fad_out/", on=pool)
...     ens = ls.simulate.scenarios(model, start, drivers, replicates=100, seed=1, on=pool)

port property

port: int

The TCP port helpers connect to (known once started).

machines property

machines: list

The computers working now: [{"name", "cores", "local", "tasks"}].

run

run(job: Job) -> JobResult

Run a Job (one at a time) and wait for it.

close

close() -> None

Stop the coordinator: helpers go back to waiting for work.

map

map(
    tool,
    items,
    *,
    on=None,
    output_dir=None,
    threads_per_task: int | None = None,
    n_workers: int | None = None,
    **common,
) -> list

Run a tool on many inputs, on this computer or on helpers, in parallel.

Parameters:

Name Type Description Default
tool

A name of TOOLS ("fad", "surface.geomorphons", "nlm.random") or the function itself.

required
items

One call per item: a dict of keyword arguments, a tuple of positional ones, or the first argument alone (a raster).

required
on

ls.helpers(): the calls run here and on the helpers of this computer's group (their arguments and results travel as data, never as code). Default: threads of this computer.

None
output_dir

Save each result there (ls.save: <output_dir>/<i>) and return the paths instead of the results.

None
threads_per_task int | None

Threads of each call (default: each computer's cores divided among the calls).

None
n_workers int | None

Calls at once on this computer, without on.

None
**common

Keyword arguments of every call.

{}

Returns:

Type Description
list

The results (or the paths of the saved results), in the order of items.

Examples:

>>> import landschaft as ls
>>> maps = ls.parallel.map("nlm.random", [dict(nrow=50, ncol=50, seed=s) for s in range(4)])
>>> [m.shape for m in maps]
[(50, 50), (50, 50), (50, 50), (50, 50)]

tiled

tiled(
    tool,
    raster,
    *args,
    halo: int | None = None,
    tile: int = 1024,
    on=None,
    output=None,
    n_workers: int | None = None,
    **kwargs,
)

Run a neighbourhood tool on a large raster tile by tile, on this computer or on helpers.

Each tile is cut with a margin (halo) of the cells its edge cells need, the tool runs on it and its result over the tile proper goes into place: the same values as on the whole raster, for tools that see only a neighbourhood (moving windows, lines of sight, slopes). A raster read lazily (ls.load_raster(path, chunks=...)) is read tile by tile.

Parameters:

Name Type Description Default
tool

A name of TOOLS or the function; its first argument is the raster.

required
raster

A DataArray (in memory or lazy).

required
*args

The tool's other arguments.

()
**kwargs

The tool's other arguments.

()
halo int | None

The margin in cells (default: what the tool needs, known for "fad", "window_metrics", "surface.geomorphons", "surface.tri", "surface.terrain", "surface.curvature", "surface.trasp", "surface.dissection" and "services.carbon", whose tiles give the same bits as the whole map; required for other tools. The window means of "surface.tpi", "vrm", "srr", "hsp" and "spherical_sd" are running sums along the rows, so their tiles agree to about 1e-12).

None
tile int

The side of a tile in cells (without its halo).

1024
on

ls.helpers(): the tiles run here and on the helpers of this computer's group.

None
output

Save the result there (ls.save) and return its path.

None
n_workers int | None

Tiles at once on this computer, without on.

None

Returns:

Type Description
The tool's result over the whole raster: a DataArray, a Dataset, or the tool's
result class when every part of it is a map (else a dict of its maps).

Examples:

>>> import numpy as np
>>> import landschaft as ls
>>> dem = ls.nlm.fbm(120, 150, fract_dim=1.4, seed=3) * 100
>>> a = ls.parallel.tiled("surface.tri", dem, tile=50)
>>> bool(np.array_equal(a.values, ls.surface.tri(dem).values, equal_nan=True))
True

call

call(tool, *args, **kwargs) -> Call

A registered tool with its arguments, the drawn ones marked by param().

Parameters:

Name Type Description Default
tool

A name of TOOLS ("surface.gravity") or the function itself.

required
*args

Its arguments; param(i) stands for the i-th parameter of each draw.

()
**kwargs

Its arguments; param(i) stands for the i-th parameter of each draw.

()

Returns:

Type Description
Call

Examples:

>>> import numpy as np
>>> import landschaft as ls
>>> src = np.zeros((20, 20)); src[10, 10] = 1
>>> f = ls.parallel.call("surface.gravity", src, exponent=ls.parallel.param(0))
>>> mc = ls.uncertainty.monte_carlo(f, [2.0], [0.1], n=50, seed=1)      # on=ls.helpers() too
>>> mc.mean.shape
(20, 20)

param

param(index: int)

The index-th drawn parameter, in the arguments of a call().

ls.parallel.TOOLS lists the tools map, tiled and monte_carlo can run by name: the analyses of ls ("fad", "mspa", ...), ls.surface ("surface.geomorphons"), ls.hydro, ls.services, ls.nlm and ls.circuit.

run_worker

run_worker(
    *,
    cores: int | None = None,
    connect: str | None = None,
    join: str | None = None,
    maps: Iterable[str] = (),
    cache_dir: str | None = None,
    cache_gb: float = 20.0,
) -> None

Lend this computer to the group: wait for tasks and run them, until Ctrl+C.

Parameters:

Name Type Description Default
cores int | None

Cores to lend (default: all but one).

None
connect str | None

The coordinator's address ("192.168.1.10" or "host:port"), when the broadcasts of the local network do not reach it.

None
join str | None

The invitation code shown by landschaft helpers invite, the first time only.

None
maps Iterable[str]

Where shared folders are mounted here, as "SRC=DST": a file the coordinator sees under SRC is read here under DST ("Z:\\=/mnt/nas").

()
cache_dir str | None

Where received input files are kept, and how much space they may take: the same data is not sent again.

None
cache_gb str | None

Where received input files are kept, and how much space they may take: the same data is not sent again.

None

invite_helpers

invite_helpers(minutes: float = 10.0, *, port: int = TCP_PORT) -> list

Show an invitation code and let other computers join this one's group with landschaft worker --join <code>, for minutes. Returns the names of those that joined.

list_helpers

list_helpers(seconds: float = 5.0) -> list

The helpers of this computer's group waiting for work now (busy ones are not looking): [{"name", "address", "cores", "version"}].

leave_group

leave_group() -> bool

Forget this computer's group (it no longer helps nor uses helpers).

open_firewall

open_firewall() -> str

Let helpers reach this computer: the Windows firewall rules for the local network.

The landschaft command

Command What it does
landschaft helpers invite [--minutes 10] show a code with which other computers join this one's group
landschaft worker --join CODE join a group (the first time) and lend this computer
landschaft worker [--cores N] [--connect HOST[:PORT]] [--map SRC=DST] [--cache-gb 20] lend this computer until Ctrl+C
landschaft helpers list the helpers waiting for work on the local network
landschaft helpers firewall Windows: let helpers reach this computer (TCP 47811, UDP 47810, local subnet)
landschaft helpers leave take this computer out of its group

Maps larger than memory

mspa_tiled

mspa_tiled(
    data,
    output: PathLike | None = None,
    *,
    connectivity: int = 8,
    edge_width: int = 1,
    transition: bool = True,
    internal: bool = True,
    tile_size: int = 6500,
    overlap: int = 2000,
    n_workers: int = 1,
    n_threads: int | None = None,
    overwrite: bool = False,
)

MSPA of a large map by overlapping tiles, identical to GTB's MSPA Tiling.

Parameters:

Name Type Description Default
data

MSPA input (0 missing, 1 background, 2 foreground): a 2-D array or DataArray (processed in memory), or the path of a GeoTIFF (processed out of core, tile by tile, into output).

required
output PathLike | None

GeoTIFF to write. For a path input it defaults to GTB's name next to the input, <name>_<FGconn>_<EdgeWidth>_<Transition>_<Intext>.tif.

None
connectivity int

MSPA parameters, see mspa(). GTB's MSPA Tiling fixes the edge width to 1.

8
edge_width int

MSPA parameters, see mspa(). GTB's MSPA Tiling fixes the edge width to 1.

8
transition int

MSPA parameters, see mspa(). GTB's MSPA Tiling fixes the edge width to 1.

8
internal int

MSPA parameters, see mspa(). GTB's MSPA Tiling fixes the edge width to 1.

8
tile_size int

Tile side including the overlap, and the overlap [pixels]. GTB uses 2000 and a tile size of 6500 on MS-Windows (9500 on Linux, 8500 on macOS).

6500
overlap int

Tile side including the overlap, and the overlap [pixels]. GTB uses 2000 and a tile size of 6500 on MS-Windows (9500 on Linux, 8500 on macOS).

6500
n_workers int

Tiles processed concurrently; each needs about 32 bytes per tile pixel.

1
n_threads int | None

Threads per tile (default: all cores but one, shared among the workers).

None
overwrite bool

Replace an existing output.

False

Returns:

Type Description
(ndarray, DataArray or Path)

The MSPA map for in-memory input; the path of the written GeoTIFF for a path input.

Examples:

>>> import landschaft as ls
>>> path = ls.mspa_tiled(ls.example_data("input"), "input_mspa_tiled.tif",
...                      tile_size=600, overlap=200)
>>> path.name
'input_mspa_tiled.tif'

tile_layout

tile_layout(
    size: int, tile_size: int = 6500, overlap: int = 2000
) -> list[tuple[int, int, int, int]]

GTB's tiles along one axis of size pixels.

Returns (read_start, read_stop, keep_start, keep_stop) per tile: the tile read for MSPA and the part of it kept in the result (map coordinates). Tiles start every tile_size - overlap pixels; the last one ends at the map border. Of each overlap, the first overlap // 2 pixels go to the earlier tile.

Parameters:

Name Type Description Default
size int

Pixels along the axis.

required
tile_size int

As in mspa_tiled().

6500
overlap int

As in mspa_tiled().

6500

Returns:

Type Description
list of tuple

Examples:

>>> import landschaft as ls
>>> ls.tile_layout(10_000)
[(0, 6500, 0, 5500), (4500, 10000, 5500, 10000)]