Data & configuration¶
Reading, preparing and writing rasters, vectors and tables, saving and plotting results, colour tables, collections of files and the thread settings shared by every tool. The page Loading and saving data shows them together.
Every example runs
The examples on these pages use the maps that ship with Landschaft
(ls.example_data) and are run as tests, outputs included.
Rasters¶
load_raster
¶
load_raster(
source,
*,
band: int | Iterable[int] | None = None,
masked: bool | str = "auto",
chunks: Any = None,
validate: str | None = None,
like=None,
resampling: str = "auto",
clip=None,
stack: bool = False,
errors: str = "raise",
recursive: bool = False,
)
Read one raster, or collect many.
One function for every raster landschaft reads: habitat and land-cover
maps, continuous surfaces, species stacks, costs. The result is a
georeferenced xarray.DataArray (CRS, transform and NoData via
.rio) that write_raster() writes back as it was.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
One raster (path or GDAL URL), a directory (its |
required | |
band
|
int | Iterable[int] | None
|
Bands to read (1-based): |
None
|
masked
|
bool | str
|
NoData handling. |
'auto'
|
chunks
|
Any
|
Single raster only: |
None
|
validate
|
str | None
|
|
None
|
like
|
A raster (DataArray or path) whose grid the result is put on,
reprojected and resampled when the CRS, resolution or extent differ,
with the coordinates of |
None
|
|
resampling
|
str
|
With |
'auto'
|
clip
|
A region (polygons as a GeoDataFrame, GeoSeries or shapely geometries,
or bounds |
None
|
|
stack
|
bool
|
Several rasters only: return a lazy |
False
|
errors
|
str
|
Several rasters only: |
'raise'
|
recursive
|
bool
|
Several rasters only: also search sub-directories. |
False
|
Returns:
| Type | Description |
|---|---|
DataArray or RasterCollection
|
For one raster, a georeferenced |
Raises:
| Type | Description |
|---|---|
InvalidInputError
|
If a file does not satisfy the requested checks. |
Examples:
A habitat map (0 missing, 1 background, 2 habitat) and a land-cover map:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> forest.shape, str(forest.dtype)
((1000, 1000), 'uint8')
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> landcover.rio.crs.to_epsg()
3035
On the grid of another raster (here a coarser one in another CRS):
>>> coarse = ls.write_raster(landcover.rio.reproject("EPSG:4326")[::4, ::4], "coarse.tif")
>>> ls.load_raster(coarse, like=landcover).rio.crs == landcover.rio.crs
True
Only a study area, read from a large raster:
>>> x0, y0, x1, y1 = landcover.rio.bounds()
>>> ls.load_raster(ls.example_data("clc"), clip=(x0, y0, x0 + 10000, y0 + 5000)).shape
(200, 400)
Checked as an MSPA input on reading:
A multi-band stack keeps its band names; NoData of floats is NaN:
>>> import numpy as np
>>> import xarray as xr
>>> habitats = xr.concat([landcover.isin([23, 24, 25]), landcover == 41], dim="band").astype("float32")
>>> habitats = habitats.assign_coords(band=["forest", "water"]).where(landcover != 1) # no data in towns
>>> _ = ls.write_raster(habitats, "stacks/habitats.tif") # creates the folder
>>> stack = ls.load_raster("stacks/habitats.tif")
>>> stack.dims, stack.band.values.tolist()
(('band', 'y', 'x'), ['forest', 'water'])
>>> bool(stack.sel(band="water").isnull().any()), ls.load_raster("stacks/habitats.tif", band=2).dims
(True, ('y', 'x'))
Many rasters at once (a folder, a pattern or a list) give a collection:
write_raster
¶
write_raster(
data,
path: PathLike,
*,
like=None,
crs=None,
transform=None,
nodata=None,
dtype=None,
band_names=None,
colormap: str | dict | None = "auto",
compress: str = "lzw",
description: str | None = None,
driver: str | None = None,
) -> Path
Write a raster: one band or several, any format GDAL writes.
One function for every raster landschaft writes. It keeps what the data
says about itself (georeferencing, NoData, band names, the colours and
parameters of landschaft's products) and stores it so that GIS software
and load_raster() read it back as it was:
- floating-point data with NaN get NaN as NoData (or
nodata, which then replaces the NaN); integers keep the.rio.nodataof the data; boolis written asuint8and 64-bit integers as the smallest integer type that holds them;- GeoTIFFs are compressed with the predictor that suits the type, tiled when
larger than 512 x 512 and BigTIFF when needed; the format follows the file
extension (
.tif,.asc,.img,.nc...) ordriver("COG": Cloud-Optimised GeoTIFF); - dask arrays are written block by block, never whole in memory;
- missing folders of
pathare created.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
2-D |
required | |
path
|
PathLike
|
Output file. |
required |
like
|
A raster (path or DataArray) on the same grid whose CRS and transform
georeference |
None
|
|
crs
|
Coordinate reference system and affine transform; they override those of
|
None
|
|
transform
|
Coordinate reference system and affine transform; they override those of
|
None
|
|
nodata
|
NoData value; overrides the one of |
None
|
|
dtype
|
Data type to write (default: the data's, see above). Floats cast to
integers get |
None
|
|
band_names
|
Band descriptions (default: a |
None
|
|
colormap
|
str | dict | None
|
|
'auto'
|
compress
|
str
|
GeoTIFF compression: |
'lzw'
|
description
|
str | None
|
TIFF image description (MSPA results record their parameters there). |
None
|
driver
|
str | None
|
GDAL format (default: from the extension of |
None
|
Returns:
| Type | Description |
|---|---|
Path
|
The written file. |
Examples:
A landschaft result keeps its colours and parameters:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> ls.write_raster(ls.mspa(forest), "forest_mspa.tif").name
'forest_mspa.tif'
A numpy array georeferenced like the input, with your own colours:
>>> import numpy as np
>>> mask = (forest.values == 2).astype("uint8")
>>> path = ls.write_raster(mask, "mask.tif", like=forest,
... colormap={0: (255, 255, 255), 1: (21, 128, 61)})
Floats with NaN: NaN is the NoData, or the value you choose:
>>> share = forest.where(forest > 0) / 2.0
>>> float(ls.load_raster(ls.write_raster(share, "share.tif")).rio.nodata)
nan
>>> ls.write_raster(share, "share.asc", nodata=-9999).name
'share.asc'
Several bands, named:
match_grid
¶
Put a raster on the grid of another: same CRS, cells and coordinates.
Rasters from different sources (a species model, a cost layer, a
land-cover map) rarely share their grid; most tools need them cell by
cell. match_grid reprojects and resamples data onto the grid of
like with GDAL's resampling (in parallel), and gives it the
coordinates of like. load_raster() does the same on reading
with like=, and reproject() with like= (which also
reprojects to a CRS, and vectors).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataArray
|
Georeferenced |
required |
like
|
The target grid: a DataArray or a raster path. |
required | |
resampling
|
str
|
|
'auto'
|
Returns:
| Type | Description |
|---|---|
DataArray
|
|
Examples:
reproject
¶
reproject(
data,
crs=None,
*,
like=None,
res=None,
resampling: str = "auto",
tolerance: float = 0.125,
densify: float | None = None,
n_threads: int | None = None,
)
Reproject a raster or vector to another CRS, or onto the grid of another raster.
Like terra's project(): a raster goes to a CRS (on the grid GDAL
suggests, or with the cell size res), or onto the grid of a reference
raster (like=: its CRS, cells and extent, so that the two combine cell
by cell); a vector goes to a CRS or to the CRS of a reference. With res
and no CRS, a raster is resampled in its own CRS.
Rasters are warped by GDAL's resampling kernels (ported to C++, run in
parallel), on the source coordinates GDAL's own transformation gives for
a fixed lattice of points of the result, interpolated in between: the
values do not depend on the number of threads, nor on whether the raster
is in memory, lazy (dask, load_raster(..., chunks=...)) or a file, so
a raster larger than memory gets the values it would get in memory. With
tolerance=0 they are GDAL's (gdalwarp -et 0), bit for bit. A lazy
raster stays lazy; a path is read window by window, never whole.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
A georeferenced |
required | |
crs
|
The target CRS: |
None
|
|
like
|
A reference: a raster (DataArray or path) whose grid a raster is put on
(CRS, cells, extent and coordinates), or whose CRS a vector takes; or a
vector (object or path) whose CRS is taken. Not with |
None
|
|
res
|
Rasters: the cell size of the result, one number or |
None
|
|
resampling
|
str
|
Rasters: |
'auto'
|
tolerance
|
float
|
Rasters: the largest error, in source cells, of the coordinates
interpolated between the points transformed exactly (default 0.125,
as |
0.125
|
densify
|
float | None
|
Vectors: add vertices so that no segment is longer than this (in the units of the source CRS) before reprojecting, so that long straight edges follow the curvature of the target CRS. Default: the vertices as they are. |
None
|
n_threads
|
int | None
|
Rasters in memory or from a file: threads (default: all cores but one); the result does not depend on it. A lazy raster stays lazy (its tasks run when computed, with dask's threads). |
None
|
Returns:
| Type | Description |
|---|---|
(DataArray, GeoDataFrame or GeoSeries)
|
Rasters: the same type and dimensions; cells outside the source are
NoData (NaN for floats, the NoData value of integer rasters, else the
largest value of unsigned types and the smallest of signed ones).
Boolean rasters become |
Examples:
>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc")) # 25 m, EPSG:3035
>>> lonlat = ls.reproject(landcover, "EPSG:4326")
>>> lonlat.rio.crs.to_epsg(), str(lonlat.dtype), int(lonlat.rio.nodata) # classes kept
(4326, 'uint8', 255)
With a cell size, in metres of UTM 32N:
Onto the grid of a reference raster, cell by cell:
>>> back = ls.reproject(lonlat, like=landcover)
>>> back.shape == landcover.shape, bool((back.x == landcover.x).all())
(True, True)
Vectors go to the CRS of a reference (raster or vector):
clip
¶
clip(
raster,
region,
*,
crop: bool = True,
all_touched: bool = False,
invert: bool = False,
) -> DataArray
Cut a raster to a region: the study area, a biome, a municipality.
The raster is cropped to the region's bounding box and the cells outside
its polygons become NoData (NaN for floats, the NoData value for integer
maps, which keep their type). The polygons are reprojected to the
raster's CRS; the cells kept are those rasterize() burns. On a lazy
raster (load_raster(..., chunks=...)) the crop is lazy too; to read
only the region from a file, use load_raster(path, clip=region).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raster
|
Georeferenced |
required | |
region
|
Polygons (GeoDataFrame, GeoSeries, shapely geometries), or bounds
|
required | |
crop
|
bool
|
Crop to the region's bounding box (default); |
True
|
all_touched
|
bool
|
Keep every cell the polygons touch (default: the cells whose centre is inside). |
False
|
invert
|
bool
|
Mask the inside of the region instead (e.g. to leave urban areas out); the extent is kept. |
False
|
Returns:
| Type | Description |
|---|---|
DataArray
|
|
Examples:
>>> import geopandas as gpd
>>> from shapely.geometry import Point
>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> x0, y0, x1, y1 = landcover.rio.bounds()
>>> area = gpd.GeoDataFrame(geometry=[Point(x0 + 10000, y0 + 10000).buffer(5000)], crs=landcover.rio.crs)
>>> study = ls.clip(landcover, area)
>>> study.shape, int(study.rio.nodata), str(study.dtype) # 10 km box, classes kept
((400, 400), 255, 'uint8')
>>> round(int((study != 255).sum()) * 25 * 25 / 1e6, 1) # km2 inside the circle
78.4
reclassify
¶
reclassify(
data,
table=None,
*,
foreground: Iterable[int] | int | Callable[[ndarray], ndarray] | None = None,
missing: Iterable[int] | int | None = None,
background: Iterable[int] | int | None = None,
others="keep",
dtype=None,
n_threads: int | None = None,
)
Give the values of a raster new values: from a table, or into a habitat map.
With a table, every value (or range of values) becomes the value the table gives it: land-cover classes into resistances or costs, classes grouped, a continuous map cut into classes. The table is
- a
dict{old: new}; a key may be a range(low, high); - a list of pairs
(old, new)or of ranges(low, high, new), which hold the valueslow <= v < high(-np.inf/np.inffor open ends); where ranges overlap, the first one wins; - a DataFrame (e.g. from
load_table()) of two columns (old, new) or three (low, high, new), or a 2-D array of the same.
With foreground (or a value, a list of values or a function in place
of the table), the result is a habitat map (0 = missing, 1 = background,
2 = habitat), the input of mspa(), fad() and the other
habitat-pattern tools.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Raster: numpy array, DataArray (dask-backed ones stay lazy), any type. |
required | |
table
|
The new values (see above). |
None
|
|
foreground
|
Iterable[int] | int | Callable[[ndarray], ndarray] | None
|
Habitat map: value(s) that become habitat (2), or a function of the
values returning a boolean mask (e.g. |
None
|
missing
|
Iterable[int] | int | None
|
Habitat map: value(s) that become missing (0). NaN and the raster's NoData value are always missing. |
None
|
background
|
Iterable[int] | int | None
|
Habitat map: value(s) that become background (1). By default every value that is neither habitat nor missing; when given, the remaining values become missing. |
None
|
others
|
Table: what values the table does not hold become: |
'keep'
|
|
dtype
|
Table: output type (default: the smallest that holds the new values, and the kept ones). |
None
|
|
n_threads
|
int | None
|
Threads (default: all cores but one). |
None
|
Returns:
| Type | Description |
|---|---|
DataArray or ndarray
|
Same type and georeferencing as |
Examples:
Land-cover classes into resistance to movement (the other classes: 50):
>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> resistance = ls.reclassify(landcover, {23: 1, 24: 1, 25: 1, 18: 10, 1: 1000}, others=50)
>>> str(resistance.dtype), sorted(int(v) for v in set(resistance.values.ravel()))
('uint16', [1, 10, 50, 1000])
Ranges of a continuous map into classes, [low, high):
>>> import numpy as np
>>> footprint = np.array([[0.0, 3.5], [12.0, np.nan]])
>>> ls.reclassify(footprint, [(0, 4, 1), (4, 10, 2), (10, np.inf, 3)])
array([[ 1., 1.],
[ 3., nan]])
Forests of a CORINE land-cover map (classes 23, 24 and 25) as a habitat map:
>>> forest = ls.reclassify(landcover, foreground=[23, 24, 25])
>>> sorted(int(v) for v in set(forest.values.ravel()))
[1, 2]
>>> morph = ls.mspa(forest)
A rule on a continuous map, water (class 41) left out as missing:
Vectors¶
load_vector
¶
load_vector(
source,
*,
layer=None,
like=None,
x: str | None = None,
y: str | None = None,
crs=None,
columns=None,
bbox=None,
)
Read a vector layer (polygons, lines, points) as a GeoDataFrame.
Any format GDAL reads (GeoPackage, Shapefile, GeoJSON, FlatGeobuf, KML...),
GeoParquet, or a table of point coordinates (CSV, Excel, Parquet): the
coordinate columns are found by name (x/y, lon/lat,
longitude/latitude, Darwin Core's decimalLongitude...).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
File path (or a GDAL URL). |
required | |
layer
|
Layer of a multi-layer file (GeoPackage...), by name or number. |
None
|
|
like
|
A raster or vector (object or path) whose CRS the result is reprojected to, so that it overlays the rasters it will be used with. |
None
|
|
x
|
str | None
|
Coordinate columns of a table (default: found by name). |
None
|
y
|
str | None
|
Coordinate columns of a table (default: found by name). |
None
|
crs
|
CRS of the coordinates of a table (default: longitude/latitude, EPSG:4326, for columns named like longitude and latitude, else none). |
None
|
|
columns
|
Attribute columns to read (default: all). |
None
|
|
bbox
|
|
None
|
Returns:
| Type | Description |
|---|---|
GeoDataFrame
|
|
Examples:
>>> import pandas as pd
>>> import landschaft as ls
>>> sites = pd.DataFrame({"name": ["a", "b"], "longitude": [10.1, 10.4], "latitude": [52.3, 52.6]})
>>> _ = ls.write_table(sites, "sites.csv")
>>> points = ls.load_vector("sites.csv") # columns found by name, EPSG:4326
>>> points.crs.to_epsg(), len(points)
(4326, 2)
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> ls.load_vector("sites.csv", like=landcover).crs.to_epsg() # on the raster's CRS
3035
write_vector
¶
Write a vector layer, the format given by the file extension.
.gpkg (GeoPackage), .shp, .geojson, .fgb (FlatGeobuf),
.kml, .parquet (GeoParquet), .csv (geometry as WKT). Missing
folders are created.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
A GeoDataFrame or GeoSeries (e.g. from |
required | |
path
|
Output file. |
required | |
**kwargs
|
Passed to geopandas ( |
{}
|
Returns:
| Type | Description |
|---|---|
Path
|
|
Examples:
>>> import geopandas as gpd
>>> from shapely.geometry import box
>>> import landschaft as ls
>>> reserves = gpd.GeoDataFrame({"name": ["north"]}, geometry=[box(0, 0, 10, 10)], crs="EPSG:3035")
>>> ls.write_vector(reserves, "out/reserves.gpkg").name
'reserves.gpkg'
>>> ls.load_vector("out/reserves.gpkg")["name"].tolist()
['north']
rasterize
¶
rasterize(
vector,
like,
*,
field: str | None = None,
value=1,
fill=0,
all_touched: bool = False,
merge: str = "replace",
dtype=None,
name: str | None = None,
) -> DataArray
Burn vector features into a raster on the grid of another raster.
Protected areas, municipalities, roads or sample sites become a raster
that lines up cell by cell with like: a mask (1 inside, 0 outside), the
values of an attribute, or the number of overlapping features. The
features are reprojected to the CRS of like first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
GeoDataFrame, GeoSeries, a list of shapely geometries or a vector path. |
required | |
like
|
The grid: a DataArray or a raster path. |
required | |
field
|
str | None
|
Attribute whose values are burnt (default: |
None
|
value
|
The value burnt when |
1
|
|
fill
|
The value of the cells no feature covers (default 0; |
0
|
|
all_touched
|
bool
|
Burn every cell a feature touches (default: the cells whose centre is inside, which does not overestimate small polygons). |
False
|
merge
|
str
|
Where features overlap: |
'replace'
|
dtype
|
Output type (default: the smallest that holds the values and |
None
|
|
name
|
str | None
|
Name of the result (default: |
None
|
Returns:
| Type | Description |
|---|---|
DataArray
|
On the grid of |
Examples:
>>> import geopandas as gpd
>>> from shapely.geometry import box
>>> import landschaft as ls
>>> landcover = ls.load_raster(ls.example_data("clc"))
>>> x0, y0 = float(landcover.x[0]), float(landcover.y[-1])
>>> reserves = gpd.GeoDataFrame({"id": [1, 2]}, crs=landcover.rio.crs,
... geometry=[box(x0, y0, x0 + 5000, y0 + 5000), box(x0 + 20000, y0, x0 + 30000, y0 + 8000)])
>>> mask = ls.rasterize(reserves, like=landcover) # 1 inside the reserves, 0 outside
>>> str(mask.dtype), int(mask.sum()) * 25 * 25 / 1e6 # km2
('uint8', 105.375)
>>> ls.rasterize(reserves, like=landcover, field="id").max().item()
2
Values by zone and at points¶
zonal_stats
¶
zonal_stats(
raster,
zones,
stats=("count", "mean", "min", "max", "std"),
*,
all_touched: bool = False,
n_threads: int | None = None,
)
Statistics of a raster in every zone: polygons, or the labels of a raster.
Mean NDVI per municipality, total population per watershed, the share of
each land-cover class per property, the median priority rank of every
protected area: computed in C++ in parallel, on every band of a stack at
once. Polygons may overlap (e.g. buffers around points): each zone gets
its own cells, the cells GDAL burns for it (rasterize()), reprojected
to the raster's CRS first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raster
|
Georeferenced |
required | |
zones
|
Polygons (GeoDataFrame, GeoSeries, shapely geometries), or a raster of zone labels on the same grid (integers; its NoData and NaN are no zone), e.g. municipalities or land-cover classes. |
required | |
stats
|
Any of |
('count', 'mean', 'min', 'max', 'std')
|
|
all_touched
|
bool
|
Polygons: every cell they touch (default: the cells whose centre is
inside, as |
False
|
n_threads
|
int | None
|
Threads (default: all cores but one); the result does not depend on it. |
None
|
Returns:
| Type | Description |
|---|---|
GeoDataFrame or DataFrame
|
For polygons, the zones with one column per statistic (per band and
statistic, |
Examples:
>>> import geopandas as gpd
>>> from shapely.geometry import box
>>> import landschaft as ls
>>> dem = ls.load_raster(ls.example_data("orelevation")) # elevation (m)
>>> x0, y0, x1, y1 = dem.rio.bounds()
>>> halves = gpd.GeoDataFrame({"part": ["west", "east"]}, crs=dem.rio.crs,
... geometry=[box(x0, y0, (x0 + x1) / 2, y1), box((x0 + x1) / 2, y0, x1, y1)])
>>> ls.zonal_stats(dem, halves, ["mean", "max", "p90"]).round(1)[["part", "mean", "max", "p90"]]
part mean max p90
0 west 894.5 1252.0 1112.0
1 east 903.5 1390.0 1228.0
Zones from a raster: the cells of three elevation classes.
extract
¶
extract(
raster,
points,
*,
method: str = "nearest",
buffer: float | None = None,
stats="mean",
all_touched: bool = False,
n_threads: int | None = None,
)
The values of a raster at points.
Habitat suitability at the occurrence records, the resistance at the
sampling sites, every band of a stack at a million points: computed in C++
in parallel. The points are reprojected to the raster's CRS. A dask-backed
raster (load_raster(..., chunks="auto")) is read only in the blocks of
rows that hold points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raster
|
Georeferenced |
required | |
points
|
GeoDataFrame or GeoSeries of points, shapely points, an |
required | |
method
|
str
|
|
'nearest'
|
buffer
|
float | None
|
A radius in the units of the raster's CRS: the statistics |
None
|
stats
|
With |
'mean'
|
|
all_touched
|
bool
|
With |
False
|
n_threads
|
int | None
|
Threads (default: all cores but one). |
None
|
Returns:
| Type | Description |
|---|---|
GeoDataFrame or DataFrame
|
The points with one column per band (named by the band, or |
Examples:
>>> import pandas as pd
>>> import landschaft as ls
>>> dem = ls.load_raster(ls.example_data("maungawhau")) # elevation (m), 10 m cells
>>> table = pd.DataFrame({"site": ["summit", "slope"], "x": [2667705.0, 2667812.0], "y": [6478895.0, 6479004.0]})
>>> sites = ls.load_vector(ls.write_table(table, "sites.csv"), crs=dem.rio.crs)
>>> ls.extract(dem, sites)[["site", "maungawhau"]]
site maungawhau
0 summit 195.0
1 slope 168.0
>>> ls.extract(dem, sites, method="bilinear")["maungawhau"].round(1).tolist()
[195.0, 169.2]
The cells within 50 m of every site:
Tables¶
load_table
¶
load_table(
source,
*,
sheet=None,
sep: str | None = None,
decimal: str | None = None,
columns=None,
encoding: str | None = None,
**kwargs,
) -> DataFrame
Read a table, the format given by the file extension.
.csv / .txt / .tsv (also .gz; the separator , ;
tab or |, a decimal comma and a Windows / Latin-1 encoding are
detected), .xlsx / .xls
(needs openpyxl), .parquet, .feather, .json, and the
attribute table of a vector file (.gpkg, .shp, .dbf...).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
File path. |
required | |
sheet
|
Excel: the sheet, by name or number (default: the first). |
None
|
|
sep
|
str | None
|
Delimited text: the column separator and the decimal mark (default: detected). |
None
|
decimal
|
str | None
|
Delimited text: the column separator and the decimal mark (default: detected). |
None
|
columns
|
Columns to read (default: all). |
None
|
|
encoding
|
str | None
|
Delimited text: the character encoding (default: UTF-8, or Windows-1252 / Latin-1 when the file is not valid UTF-8, as tables saved by Excel on Windows often are). |
None
|
**kwargs
|
Passed to the pandas reader. |
{}
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
Examples:
write_table
¶
Write a table, the format given by the file extension.
.csv (.csv.gz compressed), .tsv / .txt (tab-separated),
.xlsx (needs openpyxl), .parquet (needs pyarrow; keeps the
categorical and nullable integer columns, compact and fast for tables of
millions of rows), .feather, .json (one record per row), or, for a
GeoDataFrame, a vector format (.gpkg, .geojson, .shp,
.fgb; see write_vector()). A named index (e.g. the metric names
of a Series) is written as columns; missing folders are created.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table
|
A DataFrame, Series or GeoDataFrame. |
required | |
path
|
Output file; its extension selects the format. |
required | |
**kwargs
|
Passed to the pandas / geopandas writer. |
{}
|
Returns:
| Type | Description |
|---|---|
Path
|
The file written. |
Examples:
Saving results¶
save
¶
Save data or a result, each part in the format that suits it.
One call for everything landschaft produces:
- a raster (DataArray, numpy or dask array of 2 or 3 dimensions) becomes a
GeoTIFF, as
write_raster()writes it; - a table (DataFrame, Series) a CSV (or the format of the extension, see
write_table()), a GeoDataFrame a GeoPackage (write_vector()); - a tree a Newick file;
- a result (of
fos(),restoration_status(),zonation()... or adictof results) a folder: one GeoTIFF per map, one table per table, and the numbers and settings insummary.json. Maps that are numpy arrays get the georeferencing of a georeferenced map of the same result, or oflike. Results that have a format of their own (Zonation's output files, Biodiverse's) are written in it.
Without an extension, path gets the usual one (.tif, .csv,
.gpkg) or is a folder for a result. Missing folders are created.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
What to save. |
required | |
path
|
File, or folder for a result. |
required | |
like
|
A raster (DataArray or path) that georeferences numpy maps. |
None
|
|
table_format
|
str
|
Extension of the tables of a result: |
'csv'
|
**kwargs
|
For a single raster, table or vector: passed to its writer
( |
{}
|
Returns:
| Type | Description |
|---|---|
Path
|
The file or folder written. |
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> ls.save(ls.mspa(forest), "results/mspa").name # a raster: GeoTIFF
'mspa.tif'
>>> status = ls.restoration_status(forest)
>>> folder = ls.save(status, "results/restoration") # a result: a folder
>>> sorted(p.name for p in folder.iterdir())
['summary.json']
>>> import json
>>> json.loads((folder / "summary.json").read_text())["nr_comp"]
2850
load_result
¶
Read back a result folder written by save() (or result.save).
Every map comes back as a DataArray (load_raster()), every table as a
DataFrame (load_table()), a vector layer as a GeoDataFrame, a .npy array
as numpy, a sub-folder (a nested result) as a dict of its own, and the numbers
and settings of summary.json as they were written ("result": the name of the
result's class). The result's class itself is not rebuilt: its parts are.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
The folder. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Each part by its name (the file name without its extension). |
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> folder = ls.fos(forest).save("fos_result")
>>> parts = ls.load_result(folder)
>>> parts["result"], type(parts["map"]).__name__, type(parts["stats"]).__name__
('FOSResult', 'DataArray', 'DataFrame')
>>> parts["n_patches"] == ls.fos(forest).n_patches
True
Every result object (of fos, restoration_status, zonation...) also has
a save(folder) method that calls save.
Plotting¶
plot
¶
plot(
obj,
*,
column=None,
cmap=None,
labels=None,
categorical=None,
vmin=None,
vmax=None,
log=None,
opacity=None,
bands=None,
title=None,
basemap="satellite",
compare=False,
static=False,
max_size: int = 2048,
show=None,
**kwargs,
) -> Plot
Plot anything: a raster, a vector layer, a table or the result of a tool, as it calls for.
The form follows the data. A raster (any, or a landschaft product) goes on an interactive map over satellite imagery: a class map (integers, a product such as MSPA or FAD) in its classes' colours with their names, continuous values on a colour ramp that suits them (elevation, NDVI, priority ranks, currents on a log scale, values around zero on a diverging ramp), a stack band by band (or as true colour), a vector layer as polygons, lines or points, coloured by a column. A table becomes the chart it calls for; the result of a tool, its maps, charts and numbers together. Several rasters and vectors in a list share one map.
In a notebook the plot shows inline; elsewhere it opens in a window of its own
(pywebview, else the web browser). Rasters are reduced to the screen's
resolution before anything else (a lazy raster is read reduced), so a map of a
whole country draws in about the time of a small one. Vector layers of over
20 000 features (up to 2 million) are drawn by the graphics card (WebGL), sharp
at every zoom, the feature under the cursor read as for rasters. While Python
runs (a notebook's kernel, a script's window), zooming in past the overview
draws the part in view in full detail (LANDSCHAFT_PLOT_LIVE=0: off).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
A DataArray (2-D, or a |
required | |
column
|
Vectors: the column that colours the features. |
None
|
|
cmap
|
The colours: a palette name ( |
None
|
|
labels
|
|
None
|
|
categorical
|
|
None
|
|
vmin
|
The ramp's range (default: the 2nd and 98th percentiles) and a log scale. |
None
|
|
vmax
|
The ramp's range (default: the 2nd and 98th percentiles) and a log scale. |
None
|
|
log
|
The ramp's range (default: the 2nd and 98th percentiles) and a log scale. |
None
|
|
opacity
|
Opacity of the layers. |
None
|
|
bands
|
A stack: the bands to draw, by their labels (names, or the band numbers
of a file read by |
None
|
|
title
|
Title of the map or chart. |
None
|
|
basemap
|
|
'satellite'
|
|
compare
|
|
False
|
|
static
|
A static matplotlib figure instead (for publications and offline use; the
satellite imagery needs contextily): a stack's bands, a result's rasters or
two dates compared side by side as small multiples, with a scale bar, a
north arrow and, on a single map, longitudes and latitudes. Its options,
given as keywords: |
False
|
|
max_size
|
int
|
Pixels on the long side of every raster drawn (default 2048). |
2048
|
**kwargs
|
Options of a tool's result: |
{}
|
|
show
|
Show the plot now (default: outside notebooks, unless the environment
variable |
None
|
Returns:
| Type | Description |
|---|---|
Plot
|
Shows itself in a notebook; |
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> p = ls.plot(ls.mspa(forest)) # GuidosToolbox's colours and class names
>>> p
Plot(map)
>>> dem = ls.load_raster(ls.example_data("orelevation"))
>>> p = ls.plot(dem, cmap="terrain", title="Elevation")
>>> p.save("elevation.html").name
'elevation.html'
>>> after = forest.where(forest.x < forest.x[400], 1) # the east cut
>>> p = ls.plot([forest, after], compare=True) # drag the line to compare
Plot
¶
A plot made by plot(): interactive maps and charts (or a static figure).
It shows itself inline in a notebook (Jupyter, JupyterLab, VS Code, Colab);
elsewhere plot() opens it in a window of its own. save() writes
a self-contained HTML page (or, for a static plot, PNG, PDF, SVG).
The page Plotting shows the plots of every kind of data, and how to choose their colours.
Example data¶
example_data
¶
Path of an example data set shipped with the package.
One function for the examples of every module: open them with
load_raster(), load_vector() or load_table().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Rasters of habitat and land cover (
Surfaces (see Grid distances (see Biodiversity (see Species distributions (see Watershed model (see |
'input'
|
Returns:
| Type | Description |
|---|---|
Path
|
Path of the file. |
Examples:
Example data¶
ls.EXAMPLE_DATA describes the habitat and land-cover rasters; the
examples of the other modules are listed under example_data above:
| Name | Content |
|---|---|
"input" |
habitat map, 1000 × 1000 cells, 0 missing, 1 background, 2 forest (EPSG:4326) |
"clc" |
CORINE land cover excerpt, 2000 × 2000 cells of 25 m (EPSG:3035), classes 1–44 |
"gscinput" |
percentage map, 1990 × 1289 cells of 2430 m (EPSG:5070), 0–100, 255 missing |
Collections of rasters¶
RasterCollection
¶
RasterCollection(
infos: Iterable[RasterInfo],
*,
band: int | None = None,
validate: str | None = None,
masked: bool | str = "auto",
skipped: dict[Path, str] | None = None,
)
Bases: Sequence
Many rasters handled as one object; pixels are read only when processed.
Create it with load_raster() given a list of paths, a folder or a glob
pattern. Pass it to mspa() or batch() to process every raster in
parallel, or turn it into a lazy (file, y, x) stack with to_stack()
when all rasters share a grid.
Attributes:
| Name | Type | Description |
|---|---|---|
paths |
list of pathlib.Path
|
The files, in order. |
total_pixels |
int
|
Pixels of all rasters together. |
same_grid |
bool
|
Whether all rasters share shape, CRS and transform. |
skipped |
dict
|
Files left out by |
Examples:
>>> import landschaft as ls
>>> forest = ls.load_raster(ls.example_data("input"))
>>> _ = ls.write_raster(forest[:500], "north.tif")
>>> _ = ls.write_raster(forest[500:], "south.tif")
>>> maps = ls.load_raster("*.tif")
>>> len(maps), maps.same_grid # two different places: not the same grid
(2, False)
>>> first = maps.load(0) # one raster, as a DataArray
>>> results = ls.mspa(maps, edge_width=2) # every raster, in parallel
RasterInfo
dataclass
¶
RasterInfo(
path: Path,
height: int,
width: int,
count: int,
dtype: str,
crs: Any,
transform: Any,
nodata: float | None,
)
Header of one raster of a collection (the pixels are not read).
Attributes:
| Name | Type | Description |
|---|---|---|
path |
Path
|
The file. |
height, width, count |
int
|
Rows, columns and bands. |
dtype |
str
|
Data type of the first band. |
crs, transform |
Coordinate reference system and affine transform (rasterio objects). |
|
nodata |
float or None
|
NoData value. |
Examples:
>>> import landschaft as ls
>>> info = ls.RasterInfo.read(ls.example_data("input"))
>>> info.height, info.width, info.pixels
(1000, 1000, 1000000)
Checking inputs¶
validate_mspa_input
¶
Check that data is a habitat map MSPA can process.
MSPA expects a single-band uint8 map coded 0 = missing,
1 = background, 2 = habitat, with at least one habitat pixel and
at least one pixel that is not habitat.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
numpy array, dask array or |
required | |
spatial_only
|
bool
|
Require a 2-D map (default); otherwise leading dimensions (e.g. time) are allowed and all slices are checked together. |
True
|
Raises:
| Type | Description |
|---|---|
InvalidInputError
|
With a message that says what is wrong and how to fix it. |
Examples:
validate_fad_input
¶
Check that data is a habitat map the density and object tools accept.
A single-band uint8 map coded 0 = missing, 1 = background,
2 = habitat and 3/4 = special background, with at least one
habitat pixel (used by accounting() and fos()).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
numpy array or |
required |
Raises:
| Type | Description |
|---|---|
InvalidInputError
|
With a message that says what is wrong and how to fix it. |
Examples:
InvalidInputError
¶
Bases: ValueError
The raster cannot be processed by the requested tool.
Raised by load_raster() and the habitat-pattern tools when a map is not
coded as they expect; the message says what is wrong and how to fix it. It is
a ValueError.
Examples:
Colour tables¶
Every class map has a colour table: a {value: (red, green, blue, alpha)}
dictionary with 256 entries, embedded by write_raster. Each is also
available as a constant (ls.MSPA_CLASSES names the MSPA codes;
ls.FAD_COLORMAP, ls.LM_COLORMAP... are the tables themselves).
mspa_colormap
¶
Colour table of MSPA class maps (mspa()).
The standard colours of the product, as embedded by write_raster().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transition
|
bool
|
Colour bridges and loops that cross an edge or a perforation in their own colours (default) or as the class they cross. |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
fad_colormap
¶
Colour table of density maps of fad(), fad_multiscale() and fos() (6 classes).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
fad5_colormap
¶
Colour table of 5-class fragmentation maps (fos() with classes=5).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
lm_colormap
¶
Colour table of landscape mosaic maps (landscape_mosaic()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
accounting_colormap
¶
Colour table of object size-class maps (accounting()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
change_colormap
¶
Colour table of change maps (simple_change(), morphological_change()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
foschange_colormap
¶
Colour table of fragmentation change maps (fos_change()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
dist_colormap
¶
Colour table of distance maps (euclidean_distance()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
entropy_colormap
¶
Colour table of spatial entropy maps (spatial_entropy_map()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
contortion_colormap
¶
Colour table of contortion maps (contortion()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
resistance_colormap
¶
Colour table of resistance and isochrone maps (distance_to_resistance(), isochrone_map()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
gtn_components_colormap
¶
Colour table of network component maps (gtn_components()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
gtn_importance_colormap
¶
Colour table of network importance maps (gtn_importance()).
The standard colours of the product, as embedded by write_raster().
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
Threads¶
default_threads
¶
The number of threads a parallel function uses when not given one.
All the cores the process may use but one (at least 1), so the computer
stays responsive, or the value set with set_default_threads().
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
set_default_threads
¶
Set the default thread count of every parallel function for this process.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int | None
|
Threads to use when a function is not given |
None
|
Returns:
| Type | Description |
|---|---|
int
|
The new default. |
Examples:
max_threads
¶
Number of processors the compiled core can use.
1 when landschaft was built without OpenMP. Parallel functions use
default_threads() of them unless told otherwise.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples: