PointCloud and Raster¶
sylva.pointcloud ¶
Numpy-backed point cloud container.
PointCloud
dataclass
¶
A set of 3D points with optional per-point attributes.
Every function in Sylva that takes points takes a PointCloud and
returns a new one; nothing is modified in place. Coordinates are metres
in whatever frame the data came in (scanner, project or projected CRS).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
xyz
|
ndarray
|
|
required |
attrs
|
dict[str, ndarray]
|
Mapping of attribute name to a length- |
dict()
|
crs
|
str | None
|
Coordinate reference system of |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Notes
Attributes Sylva itself writes and reads:
| Name | Written by | Meaning |
|---|---|---|
classification |
sylva.ground.classify_ground_csf |
ASPRS codes, 2 = ground |
height |
sylva.ground.normalize_height |
height above the DTM (m) |
tree_id |
you, from sylva.trees.segment_trees |
tree number, -1 unassigned |
scan_id |
sylva.registration.merge_scans |
index of the source scan |
nx, ny, nz |
ray-cloud PLY | vector from point to sensor |
Indexing with a boolean mask, integer array or slice returns a subset
with all attributes: cloud[cloud.z > 1.3].
Examples:
>>> import numpy as np, sylva
>>> cloud = sylva.PointCloud(np.random.rand(100, 3), {"intensity": np.ones(100)})
>>> tall = cloud[cloud.z > 0.5]
bounds
property
¶
bounds: tuple[ndarray, ndarray]
Axis-aligned bounding box as (min_xyz, max_xyz), two length-3 arrays.
heights ¶
heights(attr: str = 'height') -> np.ndarray
Height above ground for each point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
attr
|
str
|
Attribute holding normalised heights. |
'height'
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
copy ¶
copy() -> PointCloud
Deep copy.
Returns:
| Type | Description |
|---|---|
PointCloud
|
A cloud whose coordinates and attribute arrays are independent of this one's. |
with_attrs ¶
with_attrs(**attrs: ndarray) -> PointCloud
Add or replace attributes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**attrs
|
ndarray
|
|
{}
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
A new cloud sharing the coordinate array; existing attributes are kept unless replaced. |
without ¶
without(*names: str) -> PointCloud
Drop attributes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*names
|
str
|
Attribute names to remove; names that are absent are ignored. |
()
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
A new cloud sharing the coordinate array. |
where ¶
where(expr: str) -> PointCloud
Points satisfying an attribute expression.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expr
|
str
|
A condition over the coordinates |
required |
Returns:
| Type | Description |
|---|---|
PointCloud
|
The points for which |
Raises:
| Type | Description |
|---|---|
ValueError
|
On a syntax error or an unknown attribute; the message gives the position. |
transform ¶
transform(matrix: ndarray) -> PointCloud
Apply a rigid or affine transform.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
matrix
|
ndarray
|
|
required |
Returns:
| Type | Description |
|---|---|
PointCloud
|
Transformed copy. Attributes are carried over unchanged, so
direction-like attributes ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
translate ¶
translate(dx: float, dy: float, dz: float = 0.0) -> PointCloud
Shift every point by a constant offset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dx
|
float
|
Offset in the units of the coordinates (m). |
required |
dy
|
float
|
Offset in the units of the coordinates (m). |
required |
dz
|
float
|
Offset in the units of the coordinates (m). |
required |
Returns:
| Type | Description |
|---|---|
PointCloud
|
Shifted copy with the same attributes and |
Raises:
| Type | Description |
|---|---|
ValueError
|
If an offset is not finite. |
Examples:
>>> local = cloud.translate(-500_000, -6_900_000)
rotate ¶
rotate(angle_deg: float, axis: str | Iterable[float] = 'z', about: Iterable[float] | None = None) -> PointCloud
Rotate every point about an axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
angle_deg
|
float
|
Angle in degrees, positive counter-clockwise when looking down
the axis towards the origin (right-handed): 90 about |
required |
axis
|
str | Iterable[float]
|
|
'z'
|
about
|
Iterable[float] | None
|
A point |
None
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
Rotated copy with the same attributes and |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the angle, axis or centre is not finite, the axis is zero or
an unknown name, or |
Examples:
>>> turned = cloud.rotate(30, about=cloud.xyz.mean(axis=0))
recentre ¶
recentre(origin: Iterable[float] | None = None) -> tuple[PointCloud, np.ndarray]
Move the coordinates close to zero.
Projected coordinates in the millions of metres leave float32 (and many viewers and meshing tools) with centimetre precision; local coordinates keep it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
origin
|
Iterable[float] | None
|
Point that becomes |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
cloud |
PointCloud
|
Shifted copy with the same attributes and |
offset |
ndarray
|
Length-3 offset that undoes the shift:
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> local, offset = cloud.recentre()
>>> restored = local.translate(*offset)
concatenate
classmethod
¶
concatenate(clouds: Iterable[PointCloud]) -> PointCloud
Stack several clouds into one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
clouds
|
Iterable[PointCloud]
|
Clouds to merge, in order. |
required |
Returns:
| Type | Description |
|---|---|
PointCloud
|
All points; only attributes present in every input are kept.
Use |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
from_array
classmethod
¶
from_array(array: ndarray, names: Mapping[int, str] | None = None) -> PointCloud
Build a cloud from a 2D array.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
array
|
ndarray
|
|
required |
names
|
Mapping[int, str] | None
|
Column index to attribute name, e.g. |
None
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
|
sylva.raster ¶
Georeferenced 2D grid for DTMs and CHMs.
Raster
dataclass
¶
A regular 2D grid in map coordinates, used for DTMs and CHMs.
data[row, col] covers x from xmin + col * resolution and y from
ymin + row * resolution, so row 0 is the southern edge (the array is
upside down compared with an image). to_geotiff and
to_ascii_grid flip it to the usual north-up order on export.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
ndarray
|
|
required |
xmin
|
float
|
Coordinates of the south-west corner of the grid (not of the first cell centre). |
required |
ymin
|
float
|
Coordinates of the south-west corner of the grid (not of the first cell centre). |
required |
resolution
|
float
|
Cell size in the units of x and y (m). |
required |
crs
|
str | None
|
Optional CRS string ( |
None
|
cell_centers ¶
cell_centers() -> tuple[np.ndarray, np.ndarray]
Cell-centre coordinates.
Returns:
| Type | Description |
|---|---|
X, Y : numpy.ndarray
|
Two |
cell_index ¶
cell_index(x, y) -> tuple[np.ndarray, np.ndarray]
Cell containing each coordinate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Coordinates (scalars or arrays of the same shape). |
required | |
y
|
Coordinates (scalars or arrays of the same shape). |
required |
Returns:
| Type | Description |
|---|---|
row, col : numpy.ndarray
|
int64 indices. They are not clipped: points outside the grid get
negative or too-large indices, so check against |
sample ¶
sample(x, y) -> np.ndarray
Interpolate the grid at arbitrary coordinates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Coordinates; flattened to 1D. |
required | |
y
|
Coordinates; flattened to 1D. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Bilinear interpolation between cell centres, one value per
coordinate. Coordinates beyond the outer cell centres take the
edge value; a NaN neighbour makes the result NaN, so call
|
fill_nearest ¶
fill_nearest() -> Raster
Fill NaN cells from the nearest valid cell.
Returns:
| Type | Description |
|---|---|
Raster
|
A copy with every NaN replaced; the input is unchanged. |
to_geotiff ¶
to_geotiff(path: str | Path, crs: str | None = None) -> None
Write a single-band float32 GeoTIFF, north-up, nodata = NaN.
Written by Sylva itself (uncompressed, in strips; BigTIFF from 4 GB), so no GDAL or rasterio is needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Output file. |
required |
crs
|
str | None
|
CRS to write (an EPSG code such as |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an empty raster or a CRS that cannot be read. |
to_ascii_grid ¶
to_ascii_grid(path: str | Path, nodata: float = -9999.0, crs: str | None = None) -> None
Write an ESRI ASCII grid (.asc), north-up, with its CRS in a
.prj beside it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Output file. |
required |
nodata
|
float
|
Value written for NaN cells ( |
-9999.0
|
crs
|
str | None
|
CRS to write, as WKT, to the |
None
|
from_ascii_grid
classmethod
¶
from_ascii_grid(path: str | Path) -> Raster
Read an ESRI ASCII grid written by to_ascii_grid or GIS software.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
|
required |
Returns:
| Type | Description |
|---|---|
Raster
|
Grid with row 0 at the south; |