sylva.synthetic¶
sylva.synthetic ¶
Small synthetic scenes for examples, tutorials and tests.
Nothing here is a forest model; the shapes are simple enough that the right answer is known (stem positions, diameters, heights, leaf area), which is what an example needs.
ALSFlight
dataclass
¶
A simulated airborne survey, from als_flight.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
PointCloud
|
The returns, in firing order, with the LAS attributes |
required |
trajectory
|
dict[str, ndarray]
|
The platform's path while the laser is on, as a table of equal
length arrays: |
required |
n_pulses
|
int
|
Pulses fired, including those clipped away or with no return. |
required |
sensor_positions ¶
sensor_positions(gps_time) -> np.ndarray
Where the scanner was when each pulse left.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gps_time
|
Times, e.g. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
|
write_tiles ¶
write_tiles(directory, size: float = 50.0, format: str = 'laz', origin=None, epsg: int | None = None)
Write the returns as square LAS/LAZ tiles.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Where to write |
required | |
size
|
float
|
Tile side (m). |
50.0
|
format
|
str
|
|
'laz'
|
origin
|
|
None
|
|
epsg
|
int | None
|
EPSG code to record in the files. |
None
|
Returns:
| Type | Description |
|---|---|
Catalog
|
The tiles written (point format 6, 1 mm quantisation). |
ForestEpochs
dataclass
¶
Two scanned epochs of one synthetic plot and their truth; see
forest_epochs.
Attributes:
| Name | Type | Description |
|---|---|---|
clouds |
list
|
The echoes of all scans of each epoch, with |
shots |
list
|
The pulses of each epoch, misses included, in the same frames. |
trees |
list
|
Per epoch, a table (dict of equal-length arrays) of the trees
standing: |
changes |
list
|
The known changes, one dict each, with |
transform |
ndarray
|
|
origins |
list
|
Scanner positions of each epoch, in its delivered frame. |
range_noise |
tuple
|
Range noise (m) of each epoch. |
of_kind ¶
of_kind(kind: str) -> list[dict]
The changes of one kind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
|
required |
Returns:
| Type | Description |
|---|---|
list of dict
|
|
ALSEpochs
dataclass
¶
Two airborne surveys of one synthetic forest; see als_epochs.
Attributes:
| Name | Type | Description |
|---|---|---|
scenes |
list
|
The scene of each survey ( |
flights |
list
|
The |
trees |
dict
|
One row per tree of the first survey (a table of equal-length
arrays): |
offset |
ndarray
|
|
gap |
tuple
|
|
growth |
float
|
Height added to the grown trees (m). |
buildings |
list
|
|
write_tiles ¶
write_tiles(directory_a, directory_b, size: float = 50.0, epsg: int | None = None)
Write both surveys as tiles, the second displaced by offset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory_a
|
Output directories. |
required | |
directory_b
|
Output directories. |
required | |
size
|
float
|
Tile side (m). |
50.0
|
epsg
|
int | None
|
EPSG code to record. |
None
|
Returns:
| Type | Description |
|---|---|
(Catalog, Catalog)
|
|
Plot
dataclass
¶
A plot from plot with its truth.
Attributes:
| Name | Type | Description |
|---|---|---|
points |
PointCloud
|
All points, with |
trees |
dict
|
Truth per tree, a table of equal-length arrays: |
cylinders |
ndarray
|
|
cylinder_tree |
ndarray
|
Tree of each cylinder. |
leaves |
dict
|
Leaf blades of all trees, as in |
dead_wood |
dict
|
Fallen logs and stumps: |
shrubs |
dict
|
Understorey shrubs: |
grass |
dict
|
Grass tufts: |
terrain |
dict
|
|
size |
float
|
Side of the plot (m); trees stand in |
qsm ¶
qsm(tree_id: int) -> QSM
The cylinders of one tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree_id
|
int
|
From |
required |
Returns:
| Type | Description |
|---|---|
QSM
|
With |
Raises:
| Type | Description |
|---|---|
KeyError
|
For a tree the plot does not have. |
ground_height ¶
ground_height(x, y) -> np.ndarray
The terrain's elevation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Coordinates (m). |
required | |
y
|
Coordinates (m). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Elevation of the plane and its micro-relief. |
SyntheticTree
dataclass
¶
A tree from tree_model with its truth.
Attributes:
| Name | Type | Description |
|---|---|---|
points |
PointCloud
|
The tree's points, with attributes |
qsm |
QSM
|
The wood as cylinders, the truth for a QSM: the points were drawn on
exactly these cylinders (on the shaped section for the stem, whose
radius is the area-equivalent one), so volumes and lengths are exact.
|
leaves |
dict
|
One entry per leaf blade (an ellipse): |
archetype |
str
|
Name of the archetype. |
base |
ndarray
|
Stem base. |
stem_bh |
ndarray
|
The stem axis at breast height (1.3 m along the stem): where a leaning stem's DBH is measured. |
dbh |
float
|
Area-equivalent diameter of the stem at breast height (m). |
height |
float
|
Highest point of wood or leaf above the base (m). |
crown_base |
float
|
Height of the lowest first-order branch that is not epicormic (m). |
crown_area |
float
|
Area of the convex hull of the crown seen from above (m²). |
crown_extent |
tuple
|
|
leaf_area |
float
|
One-sided leaf area (m²), epicormic leaves included. |
epicormic_leaf_area |
float
|
Leaf area on epicormic shoots (m²). |
lad |
str
|
Name of the leaf angle distribution the normals were drawn from. |
cylinder_epicormic |
ndarray
|
Per cylinder, whether it belongs to an epicormic shoot. |
volume_by_order ¶
volume_by_order() -> dict
Wood volume (m³) per branch order, from the cylinders.
Returns:
| Type | Description |
|---|---|
dict
|
|
length_by_order ¶
length_by_order() -> dict
Axis length (m) per branch order, from the cylinders.
Returns:
| Type | Description |
|---|---|
dict
|
|
leaf_angles ¶
leaf_angles(n_bins: int = 18) -> LeafAngleDistribution
The leaf angle distribution of the generated leaves, weighted by their area.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_bins
|
int
|
Bins over 0-90 degrees. |
18
|
Returns:
| Type | Description |
|---|---|
LeafAngleDistribution
|
Whose |
terrain_height ¶
terrain_height(x, y, slope: float = 0.05) -> np.ndarray
Ground elevation of the synthetic scenes: a gentle slope with a ripple.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Coordinates (m). |
required | |
y
|
Coordinates (m). |
required | |
slope
|
float
|
Rise per metre along x. |
0.05
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
tree ¶
tree(x: float = 0.0, y: float = 0.0, dbh: float = 0.3, height: float = 12.0, z0: float = 0.0, n_branches: int = 6, leaf_points: int = 18000, seed: int = 0) -> PointCloud
One tree: a tapered stem, n_branches limbs in the upper half and
small flat leaf discs around the limb ends.
The classification attribute is 5 for wood and 4 for leaves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float
|
Stem base position. |
0.0
|
y
|
float
|
Stem base position. |
0.0
|
z0
|
float
|
Stem base position. |
0.0
|
dbh
|
float
|
Diameter at the base (m); the stem tapers to a quarter of it. |
0.3
|
height
|
float
|
Tree height (m). |
12.0
|
n_branches
|
int
|
Limbs. |
6
|
leaf_points
|
int
|
Approximate number of leaf points (12 per leaf disc). |
18000
|
seed
|
int
|
Random seed. |
0
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
|
leaf_area ¶
leaf_area(cloud: PointCloud) -> float
True one-sided leaf area of a synthetic cloud.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cloud
|
PointCloud
|
From |
required |
Returns:
| Type | Description |
|---|---|
float
|
Area (m²) of the leaf discs, counted from |
forest ¶
forest(trees=None, size: float = 20.0, ground_points: int = 40000, margin: float = 4.0, seed: int = 0) -> PointCloud
Sloped terrain with a few trees standing on it (z is not normalised).
The ground extends margin m beyond the size m square so that no
crown overhangs the edge of the terrain.
trees is a list of (x, y, dbh, height), by default
DEFAULT_TREES. Attributes: classification (2 ground, 4 leaf,
5 wood) and tree_id (0 for ground, then 1.. in list order) as ground
truth to compare results against.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trees
|
|
None
|
|
size
|
float
|
Side of the plot square (m). |
20.0
|
ground_points
|
int
|
Points on the terrain. |
40000
|
margin
|
float
|
Terrain beyond the plot edge (m). |
4.0
|
seed
|
int
|
Random seed. |
0
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
|
scan ¶
scan(cloud: PointCloud, origin=(10.0, 10.0, 1.5), resolution_deg: float = 0.25, max_zenith_deg: float = 130.0, max_echoes: int = 2, echo_separation: float = 0.5, *, scanner: str | None = None, min_zenith_deg: float | None = None, range_noise=None, beam_divergence: float | None = None, exit_diameter: float | None = None, footprint_samples: int | None = None, mixed_pixels: bool | None = None, reflectance=None, target_radius: float | None = None, oriented: bool | None = None, detection_threshold: float | None = None, max_range: float | None = None, seed: int = 0) -> Shots
A pseudo terrestrial scan of cloud from origin.
Pulses are fired on a regular zenith / azimuth grid. The points falling in
a pulse's angular cell are its candidate targets: the nearest gives the
first echo, and further ones at least echo_separation m apart give up
to max_echoes echoes. Cells without a point are pulses with no return,
which a real scanner's point stream would not show, but which carry the
free-space information ray tracing needs. Echo attributes are copied from
the points, so classification and tree_id survive.
Beam model. Giving any of the keyword-only options below switches to
a scanner with a finite beam, cast into the points: each pulse leaves
along the centre of its angular cell as a cone of full divergence
beam_divergence from an aperture of exit_diameter, sampled by
footprint_samples equal-energy sub-beams. Each point stands for a
small patch of surface of radius target_radius: a disc facing along
its normal (the normal_x, normal_y, normal_z attributes,
which tree_model and plot provide, else a local PCA
where the points are planar), or a sphere; a sub-beam stops at the
first patch it meets. Hits closer in range than echo_separation
form one echo: with mixed_pixels at their energy-weighted mean range
(energy: the sub-beam's share times the target's reflectance), which is
how a footprint straddling an edge puts a point in the gap between
foreground and background; beyond it, separate echoes (multiple returns
within a footprint). Echoes weaker than detection_threshold are lost,
and Gaussian range noise is added along the beam. Echo positions lie on
the pulse axis, and the echoes carry, besides the attributes of their
strongest point, reflectance (dB; 0 dB is a white target filling
the footprint), footprint_fraction (share of the sub-beams in the
echo) and range_spread (m, the spread of the hits' ranges within
the echo, before noise: large for a mixed pixel). Every pulse is kept,
misses included. Leaving all of them out gives the scan above,
unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cloud
|
PointCloud
|
Scene to scan, e.g. |
required |
origin
|
Scanner position. |
(10.0, 10.0, 1.5)
|
|
resolution_deg
|
float
|
Angular step in zenith and azimuth (degrees). |
0.25
|
max_zenith_deg
|
float
|
Pulses are fired from straight up to this zenith. |
130.0
|
max_echoes
|
int
|
Echoes per pulse. |
2
|
echo_separation
|
float
|
Minimum range (m) between echoes of one pulse; with the beam model, the range resolution of the receiver. |
0.5
|
scanner
|
str | None
|
A scanner model from |
None
|
min_zenith_deg
|
float | None
|
Pulses start at this zenith (degrees); 0 by default, 30 for the scanner models. |
None
|
range_noise
|
Standard deviation of the range noise (m), or |
None
|
|
beam_divergence
|
float | None
|
Full beam divergence (mrad). |
None
|
exit_diameter
|
float | None
|
Beam diameter at the scanner (m). |
None
|
footprint_samples
|
int | None
|
Sub-beams per pulse (sunflower pattern); 7 when the beam has a size, else 1. |
None
|
mixed_pixels
|
bool | None
|
Merge hits within |
None
|
reflectance
|
Reflectance of the targets: |
None
|
|
target_radius
|
float | None
|
Radius (m) of the surface patch each point stands for; by default
1.75 times the median point spacing, which closes a surface sampled
as |
None
|
oriented
|
bool | None
|
Orient the patches as discs where a normal is known (True, the
default), which keeps silhouettes sharp; False makes every patch a
sphere, which widens every object by |
None
|
detection_threshold
|
float | None
|
Least echo energy detected (the reflectance a target filling the footprint would give); 0 by default. |
None
|
max_range
|
float | None
|
Longest range (m); unlimited by default. |
None
|
seed
|
int
|
Seed of the range noise (beam model only). |
0
|
Returns:
| Type | Description |
|---|---|
Shots
|
One pulse per angular cell, misses included. |
Examples:
>>> f = synthetic.forest()
>>> s = synthetic.scan(f) # the point-cell scan
>>> s = synthetic.scan(f, scanner="vz400", range_noise=0.005) # a finite beam
>>> s.echo_attrs["range_spread"]
als_flight ¶
als_flight(forest: PointCloud, altitude: float = 80.0, speed: float = 10.0, line_spacing: float = 40.0, heading: float = 0.0, scan_pattern: str = 'oscillating', scan_angle: float = 30.0, scan_rate: float = 80.0, pulse_rate: float = 50000.0, beam_divergence: float = 1.0, footprint_samples: int = 7, max_returns: int = 5, min_separation: float = 1.0, detection_threshold: float = 0.1, range_noise: float = 0.02, attitude=(0.5, 0.3, 0.2), target_radius: float = 0.03, terrain_slope: float = 0.05, bounds=None, clip: bool = True, margin: float = 10.0, turn_time: float = 10.0, start_time: float = 0.0, trajectory_rate: float = 100.0, seed: int = 0) -> ALSFlight
Fly a simulated airborne laser scanner over a synthetic scene.
The platform flies parallel flight lines, alternating in direction, at
constant height and speed; a mirror sweeps the beam across the track and
pulses leave at pulse_rate. A pulse is a cone of the beam divergence
sampled by footprint_samples equal-energy sub-beams, each stopping
at the first scene point it passes within target_radius of, or at the
terrain. Hits closer in range than min_separation merge into one
return (at their energy-weighted mean range), returns carrying less
than detection_threshold of the pulse energy are lost, and at most
max_returns are kept, nearest first.
The ground is the analytic surface of terrain_height (with
terrain_slope), not the scene's ground points (classification ==
2), which are ignored; so the true DTM is known exactly. Use
forest for a scene; larger scenes are a forest with more
trees over a larger size.
Geometry and timing, which the ray-based methods rely on:
- Frames: map x east, y north, z up; body x forward, y right, z down.
Heading is clockwise from north; roll positive with the right wing
down; pitch positive nose up. A body vector
bpoints alongM Rz(heading) Ry(pitch) Rx(roll) bin the map, withMtaking north-east-down to east-north-up. - The beam leaves the scanner (at the trajectory position; no lever arm
or boresight) along
b = (0, sin a, cos a)for mirror anglea(positive to the right). Every return of a pulse lies on that axis:point = position(gps_time) + range * direction; seeALSFlight.sensor_positions. scan_angleis the LAS scan anglea - roll(degrees from the vertical, including roll); the mirror angle isscan_angle + roll.- Line
k(from 0) starts atstart_time + k * (T + turn_time),Tbeing the time to fly a line, and fires pulses at1 / pulse_rateintervals from its start; every return of a pulse has the pulse'sgps_time. Roll, pitch and heading are sinusoids about level flight along the line (periods 7.3, 11.1 and 13.7 s, amplitudesattitude, phases fromseed). "oscillating": the mirror sweeps left to right and back at a constant angular rate,scan_ratesweeps per second (a zigzag on the ground)."rotating": a polygon mirror sweeps left to right only and jumps back (parallel scan lines).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
forest
|
PointCloud
|
The scene, e.g. |
required |
altitude
|
float
|
Flying height above z = 0 (m). |
80.0
|
speed
|
float
|
Ground speed (m/s). |
10.0
|
line_spacing
|
float
|
Distance between flight lines (m). The lines are centred on the
area; there are |
40.0
|
heading
|
float
|
Direction of the first line, degrees clockwise from north. |
0.0
|
scan_pattern
|
('oscillating', 'rotating')
|
Mirror motion. |
"oscillating"
|
scan_angle
|
float
|
Largest mirror angle either side of nadir (degrees, at most 75). |
30.0
|
scan_rate
|
float
|
Sweeps across the track per second. |
80.0
|
pulse_rate
|
float
|
Pulses per second. |
50000.0
|
beam_divergence
|
float
|
Full divergence (mrad): the footprint radius at range R is
|
1.0
|
footprint_samples
|
(1, 7, 19)
|
Sub-beams per pulse. |
1
|
max_returns
|
int
|
Returns kept per pulse (1-15). |
5
|
min_separation
|
float
|
Range resolution (m): hits closer than this form one return. |
1.0
|
detection_threshold
|
float
|
Share of the pulse energy a return needs (0-1). |
0.1
|
range_noise
|
float
|
Standard deviation of Gaussian noise on each return's range (m). |
0.02
|
attitude
|
Amplitudes of roll, pitch and heading (degrees, at most 10). |
(0.5, 0.3, 0.2)
|
|
target_radius
|
float
|
Radius (m) of the sphere each scene point stands for. The leaf
points of |
0.03
|
terrain_slope
|
float
|
Slope of the terrain (at most 0.5); 0.05 is that of |
0.05
|
bounds
|
|
None
|
|
clip
|
bool
|
Keep only returns inside |
True
|
margin
|
float
|
Distance (m) flown beyond the area at each end of a line. |
10.0
|
turn_time
|
float
|
Seconds between lines, laser off. |
10.0
|
start_time
|
float
|
GPS time of the first pulse (s). |
0.0
|
trajectory_rate
|
float
|
Trajectory samples per second. |
100.0
|
seed
|
int
|
Random seed (NumPy's |
0
|
Returns:
| Type | Description |
|---|---|
ALSFlight
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For settings out of range, or an altitude that would take the platform through the scene or the terrain. |
Examples:
>>> from sylva import synthetic
>>> flight = synthetic.als_flight(synthetic.forest(), pulse_rate=20_000)
>>> cat = flight.write_tiles("tiles/", size=10.0)
forest_epochs ¶
forest_epochs(n_trees: int = 16, size: float = 30.0, deaths: int = 2, recruits: int = 2, replaced: int = 1, small_increments: int = 2, dbh_increment: tuple[float, float] = (0.012, 0.004), height_increment: tuple[float, float] = (0.6, 0.2), branch_removals: int = 1, foliage_box=None, foliage_fraction: float = 0.5, tree_shift: float = 0.0, offset=None, range_noise: tuple[float, float] = (0.003, 0.005), scan_positions=None, scan_jitter: float = 0.5, resolution_deg: float = 0.25, max_echoes: int = 1, ground_density: float = 60.0, min_spacing: float = 2.5, seed: int = 0) -> ForestEpochs
Two epochs of one synthetic plot with known changes, each scanned.
Every tree is a fixed structure (a tapered stem, limbs at fixed heights,
leaf discs at fixed offsets from the limb tips) sampled afresh in each
epoch. Between the epochs survivors grow by a known DBH and height
increment (a uniform layer of wood along the stem, a longer leader and
proportionally longer limbs); deaths trees are gone, recruits new
small trees stand (replaced of them 0.3 to 0.6 m from a dead stem, as
after felling), branch_removals survivors lose their largest limb,
small_increments survivors grow by only 0.5 mm in DBH and 1 cm in
height, and foliage_fraction of the leaf discs in foliage_box
disappear. Each epoch is scanned from scan_positions with
scan (so occlusion is real) and Gaussian range noise; the epoch-2
scanners stand scan_jitter m (one sigma) from the epoch-1 ones, and
epoch 2 is delivered in a frame displaced by offset, as an
independently registered revisit would be.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_trees
|
int
|
Trees in epoch 1, with DBH drawn from 0.15 to 0.5 m and height from an allometry (about 12 to 20 m). |
16
|
size
|
float
|
Side of the square plot (m); stems stand at least 2 m inside it. |
30.0
|
deaths
|
int
|
Trees lost, trees gained, and of those the felled-and-replaced pairs. |
2
|
recruits
|
int
|
Trees lost, trees gained, and of those the felled-and-replaced pairs. |
2
|
replaced
|
int
|
Trees lost, trees gained, and of those the felled-and-replaced pairs. |
2
|
small_increments
|
int
|
Survivors with increments far below what a scan can detect. |
2
|
dbh_increment
|
tuple[float, float]
|
Mean and standard deviation (m) of the survivors' increments. |
(0.012, 0.004)
|
height_increment
|
tuple[float, float]
|
Mean and standard deviation (m) of the survivors' increments. |
(0.012, 0.004)
|
branch_removals
|
int
|
Survivors that lose their largest limb and its leaves. |
1
|
foliage_box
|
|
None
|
|
foliage_fraction
|
float
|
Share of the leaf discs in the box that disappear. |
0.5
|
tree_shift
|
float
|
Horizontal displacement (m, random direction) of every survivor's stem between the epochs, to test matching; 0 keeps stems in place. |
0.0
|
offset
|
|
None
|
|
range_noise
|
tuple[float, float]
|
Range noise (m, one sigma) of epoch 1 and epoch 2. |
(0.003, 0.005)
|
scan_positions
|
Scanner positions as |
None
|
|
scan_jitter
|
float
|
Standard deviation (m) of the epoch-2 scanners' offsets. |
0.5
|
resolution_deg
|
float
|
Angular step of the scans (degrees). |
0.25
|
max_echoes
|
int
|
Echoes recorded per pulse. |
1
|
ground_density
|
float
|
Terrain points per m² before scanning. |
60.0
|
min_spacing
|
float
|
Least distance between stems (m). |
2.5
|
seed
|
int
|
Random seed; the same seed gives the same epochs. |
0
|
Returns:
| Type | Description |
|---|---|
ForestEpochs
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the counts are inconsistent (more deaths than trees, more replaced than deaths or recruits) or the trees cannot be placed. |
Examples:
>>> ep = synthetic.forest_epochs(seed=1)
>>> ref, new = ep.clouds
>>> [c["tree_id"] for c in ep.of_kind("death")]
waveforms ¶
waveforms(shots: Shots, gps_time=None, pulse_width: float = 1.5, interval: float = 1.0, n_samples: int = 120, margin: float = 3.0, start_range: float | None = None, background: float = 10.0, noise: float = 1.0, digitise: bool = True, bits: int = 16, amplitude: float = 100.0, metres_per_ns: float = 0.299792458 / 2.0, seed: int = 0)
Full waveforms of pulses whose targets are known.
The received waveform is the system pulse, a Gaussian of standard
deviation pulse_width, convolved with the targets along the beam
(Wagner et al. 2006): each echo of shots is a target at its range,
with the peak amplitude of the echo attribute amplitude (for a point
target) and the depth of the echo attribute extent (standard
deviation along the beam, m; 0 if absent). A target of depth e
gives a Gaussian echo of width s = sqrt(pulse_width^2 +
(e / metres_per_ns)^2) and, its energy being kept, peak
amplitude * pulse_width / s. The echoes are added to a constant
background with Gaussian noise and, with digitise, rounded
and clipped to 0 .. 2^bits - 1 as a digitiser would.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shots
|
Shots
|
Pulses and their targets; a shot without an echo gives a waveform of background and noise only. |
required |
gps_time
|
Time of each shot; the shot index by default. |
None
|
|
pulse_width
|
float
|
Standard deviation of the system pulse (ns). A 3.5 ns full width at half maximum is 1.5 ns. |
1.5
|
interval
|
float
|
Sampling interval (ns). |
1.0
|
n_samples
|
int
|
Samples per waveform. |
120
|
margin
|
float
|
Range (m) before a shot's first echo at which its record starts (a shot without an echo starts at the origin). |
3.0
|
start_range
|
float | None
|
Range (m) at which every record starts, instead of |
None
|
background
|
float
|
Background level and noise standard deviation (sample units). |
10.0
|
noise
|
float
|
Background level and noise standard deviation (sample units). |
10.0
|
digitise
|
bool
|
Round and clip the samples. |
True
|
bits
|
int
|
Digitiser resolution. |
16
|
amplitude
|
float
|
Peak of echoes without an |
100.0
|
metres_per_ns
|
float
|
Range per nanosecond of round-trip time (m/ns). |
0.299792458 / 2.0
|
seed
|
int
|
Seed of the noise. |
0
|
Returns:
| Name | Type | Description |
|---|---|---|
waveforms |
Waveforms
|
One returning waveform per shot, anchored at its origin. |
truth |
Echoes
|
The echoes they contain: time, peak amplitude and width after the convolution, position and range. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For non-positive widths, intervals or sample counts, negative noise,
or |
stand ¶
stand(n_trees: int, size: float = 100.0, min_spacing: float = 4.0, heights=(10.0, 25.0), seed: int = 0) -> list[tuple[float, float, float, float]]
Random trees for forest: a stand of a given density.
Stems are drawn uniformly in the size m square and rejected when
closer than min_spacing to one already placed; heights are uniform
in heights and dbh = 0.1 + 0.015 * height.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_trees
|
int
|
Trees to place. |
required |
size
|
float
|
Side of the square (m). |
100.0
|
min_spacing
|
float
|
Smallest distance between stems (m). |
4.0
|
heights
|
|
(10.0, 25.0)
|
|
seed
|
int
|
Random seed (NumPy's |
0
|
Returns:
| Type | Description |
|---|---|
list of (x, y, dbh, height)
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For bad settings, or if the trees cannot be placed that far apart. |
crown_forest ¶
crown_forest(trees, size: float = 100.0, shape: str = 'ellipsoid', crown_radius: float = 0.25, crown_length: float = 0.5, density: float = 40.0, ground_points: int = 40000, margin: float = 4.0, seed: int = 0) -> PointCloud
A scene of trees with solid crowns, for airborne lidar.
Each tree (x, y, dbh, height) (e.g. from stand) has a stem
(points on a cylinder of diameter dbh, classification 5) from
the terrain up to its crown, and a crown filled uniformly with
density leaf points per m³ (classification 4): an ellipsoid of
revolution or a cone of radius crown_radius * height and length
crown_length * height, its top height above the terrain at the
stem. The trees of forest carry their leaves in clusters at the
ends of a few limbs, which suits terrestrial scanning; these have the
closed outline that airborne tree detection assumes, and a projected
crown area of pi * (crown_radius * height) ** 2.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trees
|
|
required | |
size
|
float
|
Terrain as in |
100.0
|
ground_points
|
float
|
Terrain as in |
100.0
|
margin
|
float
|
Terrain as in |
100.0
|
shape
|
('ellipsoid', 'cone')
|
Crown shape. |
"ellipsoid"
|
crown_radius
|
float
|
Crown radius and length as fractions of the tree height
( |
0.25
|
crown_length
|
float
|
Crown radius and length as fractions of the tree height
( |
0.25
|
density
|
float
|
Leaf points per m³ of crown. With the default |
40.0
|
seed
|
int
|
Random seed; the crown of tree |
0
|
Returns:
| Type | Description |
|---|---|
PointCloud
|
With |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a non-positive radius or density, a crown length outside (0, 1], or trees without finite positions and positive heights. |
forest_trees ¶
forest_trees(forest: PointCloud, terrain_slope: float = 0.05) -> dict
The true trees of a scene from forest, to check tree
detection and crown delineation against.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
forest
|
PointCloud
|
The scene, from |
required |
terrain_slope
|
float
|
Slope of the terrain under it (0.05 for |
0.05
|
Returns:
| Type | Description |
|---|---|
dict
|
Columns in order of |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the scene has no |
als_epochs ¶
als_epochs(n_trees: int = 100, size: float = 100.0, removed: int = 4, gap=(70.0, 70.0, 5), growth: float = 1.0, offset=(0.3, -0.2, 0.15), buildings: bool = True, flight_a: dict | None = None, flight_b: dict | None = None, seed: int = 0) -> ALSEpochs
Two airborne surveys of a synthetic forest with known changes.
A stand of n_trees (~sylva.synthetic.stand, 12 to 25 m,
stems at least 4 m apart) in the west size m of a square of size +
20 m, with two gabled buildings east of it (one ridge running north,
one east, so that their roofs have slopes of four aspects), is flown
twice with ~sylva.synthetic.als_flight. Between the surveys
- the
gap[2]trees nearest togap[:2]are felled, opening a gap; removedother trees are felled singly;- the trees in the west half grow by
growthm (their crowns scale with their height), the others are unchanged, point for point; - the second survey is flown by another sensor (by default 120 m above ground at 2,500 pulses/s with 40 m between lines, against 80 m at 4,000 pulses/s and 30 m: about half the pulse density and a 50 % wider footprint);
- its tiles are delivered displaced by
offset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_trees
|
int
|
Trees and side (m) of the forest. |
100
|
size
|
int
|
Trees and side (m) of the forest. |
100
|
removed
|
int
|
Trees felled singly. |
4
|
gap
|
|
(70.0, 70.0, 5)
|
|
growth
|
float
|
Height growth (m) of the trees in the west half. |
1.0
|
offset
|
|
(0.3, -0.2, 0.15)
|
|
buildings
|
bool
|
Add the two buildings. |
True
|
flight_a
|
dict | None
|
Settings of |
None
|
flight_b
|
dict | None
|
Settings of |
None
|
seed
|
int
|
Random seed. |
0
|
Returns:
| Type | Description |
|---|---|
ALSEpochs
|
|
archetype ¶
archetype(name: str) -> dict
The architectural constants of an archetype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
One of |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
plot ¶
plot(size: float = 30.0, density: float = 600.0, dbh=('weibull', 1.8, 0.22), min_dbh: float = 0.07, archetypes='broadleaf', *, height_noise: float = 0.1, slope: float = 0.1, aspect: float = 90.0, roughness: float = 0.05, roughness_length: float = 2.0, shrubs: float = 400.0, grass_cover: float = 0.2, grass_height: float = 0.5, logs: float = 60.0, stumps: float = 40.0, ground_density: float = 400.0, margin: float = 2.0, point_density: float = 1000.0, max_order: int = 3, lad=None, epicormic: float = 0.0, seed: int = 0) -> Plot
A forest plot of tree_model trees, with its truth.
round(density * size² / 10⁴) diameters are drawn from the
distribution and truncated below at min_dbh; each tree takes an
archetype by weight, a height from the archetype's Chapman-Richards
curve 1.3 + a (1 - exp(-b D))^c (D in cm) times a lognormal scatter
exp(N(0, height_noise)), and the archetype's crown and leaf area
allometries. Stems are placed uniformly at random, largest first, never
closer than 0.75 (D_i + D_j) + 0.2 m. The terrain is a plane rising
by slope per metre towards aspect plus a Gaussian random field
(64 Fourier modes) of standard deviation roughness and correlation
length roughness_length. Shrubs (shrub archetype, 0.5 to 2.5 m),
grass tufts (15 cm radius, blades up to grass_height), fallen logs
(2 to 8 m long, 5 to 25 cm radius, lying on the ground) and stumps (0.2
to 1 m tall, 10 to 30 cm radius) are scattered over the plot. Nothing is
drawn below the terrain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
float
|
Side of the square plot (m). |
30.0
|
density
|
float
|
Stems per hectare. |
600.0
|
dbh
|
Diameter distribution: |
('weibull', 1.8, 0.22)
|
|
min_dbh
|
float
|
Smallest diameter (m). |
0.07
|
archetypes
|
An archetype name, or |
'broadleaf'
|
|
height_noise
|
float
|
Standard deviation of |
0.1
|
slope
|
float
|
Rise per metre (e.g. 0.1 is 5.7 degrees). |
0.1
|
aspect
|
float
|
Direction of steepest ascent (degrees clockwise from north, +y). |
90.0
|
roughness
|
float
|
Standard deviation of the micro-relief (m). |
0.05
|
roughness_length
|
float
|
Correlation length of the micro-relief (m). |
2.0
|
shrubs
|
float
|
Shrubs per hectare. |
400.0
|
grass_cover
|
float
|
Fraction of the plot covered by grass tufts. |
0.2
|
grass_height
|
float
|
Tallest grass (m). |
0.5
|
logs
|
float
|
Fallen logs and stumps per hectare. |
60.0
|
stumps
|
float
|
Fallen logs and stumps per hectare. |
60.0
|
ground_density
|
float
|
Terrain points per m², over the plot and |
400.0
|
margin
|
float
|
Terrain beyond the plot edge (m). |
2.0
|
point_density
|
float
|
Points per m² of wood, leaf, understorey and dead wood. |
1000.0
|
max_order
|
int
|
Highest branch order of the trees (1 to 4). |
3
|
lad
|
Leaf angle distribution of every tree, as in |
None
|
|
epicormic
|
float
|
Epicormic shoots per metre of bole on every tree. |
0.0
|
seed
|
int
|
Random seed. Trees are grown in parallel, each with its own seed, so the plot does not depend on the number of threads. |
0
|
Returns:
| Type | Description |
|---|---|
Plot
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For unknown names, values out of range, or more stems than fit in the plot without overlap. |
Examples:
>>> p = synthetic.plot(size=20, density=800, archetypes={"eucalypt": 2, "broadleaf": 1})
>>> p.trees["dbh"], p.stem_density, p.basal_area
>>> p.points.attrs["label"] # 1 ground ... 6 dead wood
scanner_preset ¶
scanner_preset(name: str) -> dict
Field of view and beam of a scanner model, as synthetic.scan
takes them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
One of |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown model. |
tree_model ¶
tree_model(archetype: str = 'broadleaf', x: float = 0.0, y: float = 0.0, z0: float = 0.0, dbh: float = 0.3, height: float = 15.0, *, crown_radius: float | None = None, crown_base: float | None = None, max_order: int = 3, lean: float | None = None, sweep: float | None = None, butt_swell: float | None = None, buttresses: int = 0, buttress_height: float = 1.0, buttress_extent: float = 0.5, ellipticity: float = 0.0, bark_depth: float = 0.0, leaf_area: float | None = None, leaf_size=None, lad=None, epicormic: float = 0.0, point_density: float = 2000.0, pipe_exponent: float = 2.0, min_radius: float = 0.0015, breast_height: float = 1.3, seed: int = 0) -> SyntheticTree
A tree with a realistic architecture and its truth.
The stem leans and curves smoothly, tapers, and swells at the butt; it
can be elliptical, fissured and buttressed. First-order branches leave
it from the crown base up (in whorls for the conifer) with a length set
by the archetype's crown envelope; forking archetypes end the stem in
codominant limbs. Each branch carries children up to max_order,
each a fixed fraction of its parent's length (shorter towards the tip),
at the archetype's branching angle, bending up or drooping. Radii follow
the pipe model: above the lowest branch every segment's cross-sectional
area is proportional to the leaf area it carries (Shinozaki et al.
1964), continuous with the stem taper below. Leaves are elliptical
blades on the terminal twigs, with normals drawn from the leaf angle
distribution. Points are drawn at point_density per m² on the wood
surface and on one side of each leaf.
Archetypes (archetype lists their constants):
"broadleaf"
crown from 35 % of the height, ellipsoidal, the stem forking into
two limbs at 80 %; planophile leaves 10 × 5 cm.
"conifer"
a central leader to the top with whorls of five branches from 25 %
of the height, a conical crown, drooping branches; spherical
distribution of 5 × 1.2 cm blades standing in for shoots.
"eucalypt"
a clear bole to 55 % of the height, three ascending limbs from 65 %,
a sparse crown (low leaf area) of pendulous (erectophile) 15 × 3 cm
leaves, a noticeable lean.
"savanna"
a short bole forking at 30 % of the height into three spreading
limbs, a wide flat crown, small planophile leaves.
"shrub"
several stems from near the ground; dbh is the stem diameter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
archetype
|
str
|
One of |
'broadleaf'
|
x
|
float
|
Stem base. |
0.0
|
y
|
float
|
Stem base. |
0.0
|
z0
|
float
|
Stem base. |
0.0
|
dbh
|
float
|
Diameter at |
0.3
|
height
|
float
|
Target height (m); the realised height is |
15.0
|
crown_radius
|
float | None
|
Crown radius (m); by default |
None
|
crown_base
|
float | None
|
Height of the lowest branch (m); by default the archetype's. |
None
|
max_order
|
int
|
Highest branch order, 1 to 4. |
3
|
lean
|
float | None
|
Lean of the stem (degrees, towards a random azimuth); by default the archetype's. |
None
|
sweep
|
float | None
|
Slope amplitude of the stem's smooth curvature (0 for straight). |
None
|
butt_swell
|
float | None
|
Extra radius at the ground relative to the taper (e.g. 0.3). |
None
|
buttresses
|
int
|
Number of buttress flanges (0 for none). |
0
|
buttress_height
|
float
|
Height the flanges reach (m). |
1.0
|
buttress_extent
|
float
|
Relative extra radius at the crest of a flange at the ground. |
0.5
|
ellipticity
|
float
|
|
0.0
|
bark_depth
|
float
|
Depth of bark fissures (m); the area stays that of the round section. |
0.0
|
leaf_area
|
float | None
|
One-sided leaf area of the crown (m²); by default
|
None
|
leaf_size
|
|
None
|
|
lad
|
Leaf angle distribution: a de Wit name ( |
None
|
|
epicormic
|
float
|
Epicormic shoots per metre of bole below the crown (0 for none), as on eucalypts resprouting after fire. |
0.0
|
point_density
|
float
|
Points per m² of wood surface and of leaf. |
2000.0
|
pipe_exponent
|
float
|
Exponent |
2.0
|
min_radius
|
float
|
Smallest twig radius (m). |
0.0015
|
breast_height
|
float
|
Distance along the stem at which |
1.3
|
seed
|
int
|
Random seed; the same seed gives the same tree on every machine. |
0
|
Returns:
| Type | Description |
|---|---|
SyntheticTree
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown archetype or leaf angle distribution, or a size that is not positive. |
Examples:
>>> t = synthetic.tree_model("eucalypt", dbh=0.5, height=30, epicormic=4, seed=1)
>>> t.dbh, t.height, t.wood_volume, t.leaf_area
>>> t.volume_by_order(), t.leaf_angles().mean_deg
Realistic trees and plots¶
tree_model, plot, archetype and scanner_preset are available from
sylva.synthetic; see Synthetic trees, plots and scans.
sylva.synthetic.model ¶
Realistic synthetic trees and plots, with their truth.
These are available from sylva.synthetic as tree_model and
plot. A tree is grown from an archetype with recursive branching,
allometric lengths and angles, pipe-model radii, a leaning and curving
stem, and leaves of known size and orientation; a plot mixes such trees
at a given stem density and diameter distribution on rough, sloping
terrain with understorey, grass, fallen logs and stumps. Every result
carries its truth: the wood as a cylinder table (a ~sylva.qsm.QSM),
the leaves as blades, DBH, height, crown and per-point labels.
SyntheticTree
dataclass
¶
A tree from tree_model with its truth.
Attributes:
| Name | Type | Description |
|---|---|---|
points |
PointCloud
|
The tree's points, with attributes |
qsm |
QSM
|
The wood as cylinders, the truth for a QSM: the points were drawn on
exactly these cylinders (on the shaped section for the stem, whose
radius is the area-equivalent one), so volumes and lengths are exact.
|
leaves |
dict
|
One entry per leaf blade (an ellipse): |
archetype |
str
|
Name of the archetype. |
base |
ndarray
|
Stem base. |
stem_bh |
ndarray
|
The stem axis at breast height (1.3 m along the stem): where a leaning stem's DBH is measured. |
dbh |
float
|
Area-equivalent diameter of the stem at breast height (m). |
height |
float
|
Highest point of wood or leaf above the base (m). |
crown_base |
float
|
Height of the lowest first-order branch that is not epicormic (m). |
crown_area |
float
|
Area of the convex hull of the crown seen from above (m²). |
crown_extent |
tuple
|
|
leaf_area |
float
|
One-sided leaf area (m²), epicormic leaves included. |
epicormic_leaf_area |
float
|
Leaf area on epicormic shoots (m²). |
lad |
str
|
Name of the leaf angle distribution the normals were drawn from. |
cylinder_epicormic |
ndarray
|
Per cylinder, whether it belongs to an epicormic shoot. |
volume_by_order ¶
volume_by_order() -> dict
Wood volume (m³) per branch order, from the cylinders.
Returns:
| Type | Description |
|---|---|
dict
|
|
length_by_order ¶
length_by_order() -> dict
Axis length (m) per branch order, from the cylinders.
Returns:
| Type | Description |
|---|---|
dict
|
|
leaf_angles ¶
leaf_angles(n_bins: int = 18) -> LeafAngleDistribution
The leaf angle distribution of the generated leaves, weighted by their area.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_bins
|
int
|
Bins over 0-90 degrees. |
18
|
Returns:
| Type | Description |
|---|---|
LeafAngleDistribution
|
Whose |
Plot
dataclass
¶
A plot from plot with its truth.
Attributes:
| Name | Type | Description |
|---|---|---|
points |
PointCloud
|
All points, with |
trees |
dict
|
Truth per tree, a table of equal-length arrays: |
cylinders |
ndarray
|
|
cylinder_tree |
ndarray
|
Tree of each cylinder. |
leaves |
dict
|
Leaf blades of all trees, as in |
dead_wood |
dict
|
Fallen logs and stumps: |
shrubs |
dict
|
Understorey shrubs: |
grass |
dict
|
Grass tufts: |
terrain |
dict
|
|
size |
float
|
Side of the plot (m); trees stand in |
qsm ¶
qsm(tree_id: int) -> QSM
The cylinders of one tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree_id
|
int
|
From |
required |
Returns:
| Type | Description |
|---|---|
QSM
|
With |
Raises:
| Type | Description |
|---|---|
KeyError
|
For a tree the plot does not have. |
ground_height ¶
ground_height(x, y) -> np.ndarray
The terrain's elevation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Coordinates (m). |
required | |
y
|
Coordinates (m). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Elevation of the plane and its micro-relief. |
archetype ¶
archetype(name: str) -> dict
The architectural constants of an archetype.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
One of |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
tree_model ¶
tree_model(archetype: str = 'broadleaf', x: float = 0.0, y: float = 0.0, z0: float = 0.0, dbh: float = 0.3, height: float = 15.0, *, crown_radius: float | None = None, crown_base: float | None = None, max_order: int = 3, lean: float | None = None, sweep: float | None = None, butt_swell: float | None = None, buttresses: int = 0, buttress_height: float = 1.0, buttress_extent: float = 0.5, ellipticity: float = 0.0, bark_depth: float = 0.0, leaf_area: float | None = None, leaf_size=None, lad=None, epicormic: float = 0.0, point_density: float = 2000.0, pipe_exponent: float = 2.0, min_radius: float = 0.0015, breast_height: float = 1.3, seed: int = 0) -> SyntheticTree
A tree with a realistic architecture and its truth.
The stem leans and curves smoothly, tapers, and swells at the butt; it
can be elliptical, fissured and buttressed. First-order branches leave
it from the crown base up (in whorls for the conifer) with a length set
by the archetype's crown envelope; forking archetypes end the stem in
codominant limbs. Each branch carries children up to max_order,
each a fixed fraction of its parent's length (shorter towards the tip),
at the archetype's branching angle, bending up or drooping. Radii follow
the pipe model: above the lowest branch every segment's cross-sectional
area is proportional to the leaf area it carries (Shinozaki et al.
1964), continuous with the stem taper below. Leaves are elliptical
blades on the terminal twigs, with normals drawn from the leaf angle
distribution. Points are drawn at point_density per m² on the wood
surface and on one side of each leaf.
Archetypes (archetype lists their constants):
"broadleaf"
crown from 35 % of the height, ellipsoidal, the stem forking into
two limbs at 80 %; planophile leaves 10 × 5 cm.
"conifer"
a central leader to the top with whorls of five branches from 25 %
of the height, a conical crown, drooping branches; spherical
distribution of 5 × 1.2 cm blades standing in for shoots.
"eucalypt"
a clear bole to 55 % of the height, three ascending limbs from 65 %,
a sparse crown (low leaf area) of pendulous (erectophile) 15 × 3 cm
leaves, a noticeable lean.
"savanna"
a short bole forking at 30 % of the height into three spreading
limbs, a wide flat crown, small planophile leaves.
"shrub"
several stems from near the ground; dbh is the stem diameter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
archetype
|
str
|
One of |
'broadleaf'
|
x
|
float
|
Stem base. |
0.0
|
y
|
float
|
Stem base. |
0.0
|
z0
|
float
|
Stem base. |
0.0
|
dbh
|
float
|
Diameter at |
0.3
|
height
|
float
|
Target height (m); the realised height is |
15.0
|
crown_radius
|
float | None
|
Crown radius (m); by default |
None
|
crown_base
|
float | None
|
Height of the lowest branch (m); by default the archetype's. |
None
|
max_order
|
int
|
Highest branch order, 1 to 4. |
3
|
lean
|
float | None
|
Lean of the stem (degrees, towards a random azimuth); by default the archetype's. |
None
|
sweep
|
float | None
|
Slope amplitude of the stem's smooth curvature (0 for straight). |
None
|
butt_swell
|
float | None
|
Extra radius at the ground relative to the taper (e.g. 0.3). |
None
|
buttresses
|
int
|
Number of buttress flanges (0 for none). |
0
|
buttress_height
|
float
|
Height the flanges reach (m). |
1.0
|
buttress_extent
|
float
|
Relative extra radius at the crest of a flange at the ground. |
0.5
|
ellipticity
|
float
|
|
0.0
|
bark_depth
|
float
|
Depth of bark fissures (m); the area stays that of the round section. |
0.0
|
leaf_area
|
float | None
|
One-sided leaf area of the crown (m²); by default
|
None
|
leaf_size
|
|
None
|
|
lad
|
Leaf angle distribution: a de Wit name ( |
None
|
|
epicormic
|
float
|
Epicormic shoots per metre of bole below the crown (0 for none), as on eucalypts resprouting after fire. |
0.0
|
point_density
|
float
|
Points per m² of wood surface and of leaf. |
2000.0
|
pipe_exponent
|
float
|
Exponent |
2.0
|
min_radius
|
float
|
Smallest twig radius (m). |
0.0015
|
breast_height
|
float
|
Distance along the stem at which |
1.3
|
seed
|
int
|
Random seed; the same seed gives the same tree on every machine. |
0
|
Returns:
| Type | Description |
|---|---|
SyntheticTree
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown archetype or leaf angle distribution, or a size that is not positive. |
Examples:
>>> t = synthetic.tree_model("eucalypt", dbh=0.5, height=30, epicormic=4, seed=1)
>>> t.dbh, t.height, t.wood_volume, t.leaf_area
>>> t.volume_by_order(), t.leaf_angles().mean_deg
plot ¶
plot(size: float = 30.0, density: float = 600.0, dbh=('weibull', 1.8, 0.22), min_dbh: float = 0.07, archetypes='broadleaf', *, height_noise: float = 0.1, slope: float = 0.1, aspect: float = 90.0, roughness: float = 0.05, roughness_length: float = 2.0, shrubs: float = 400.0, grass_cover: float = 0.2, grass_height: float = 0.5, logs: float = 60.0, stumps: float = 40.0, ground_density: float = 400.0, margin: float = 2.0, point_density: float = 1000.0, max_order: int = 3, lad=None, epicormic: float = 0.0, seed: int = 0) -> Plot
A forest plot of tree_model trees, with its truth.
round(density * size² / 10⁴) diameters are drawn from the
distribution and truncated below at min_dbh; each tree takes an
archetype by weight, a height from the archetype's Chapman-Richards
curve 1.3 + a (1 - exp(-b D))^c (D in cm) times a lognormal scatter
exp(N(0, height_noise)), and the archetype's crown and leaf area
allometries. Stems are placed uniformly at random, largest first, never
closer than 0.75 (D_i + D_j) + 0.2 m. The terrain is a plane rising
by slope per metre towards aspect plus a Gaussian random field
(64 Fourier modes) of standard deviation roughness and correlation
length roughness_length. Shrubs (shrub archetype, 0.5 to 2.5 m),
grass tufts (15 cm radius, blades up to grass_height), fallen logs
(2 to 8 m long, 5 to 25 cm radius, lying on the ground) and stumps (0.2
to 1 m tall, 10 to 30 cm radius) are scattered over the plot. Nothing is
drawn below the terrain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
float
|
Side of the square plot (m). |
30.0
|
density
|
float
|
Stems per hectare. |
600.0
|
dbh
|
Diameter distribution: |
('weibull', 1.8, 0.22)
|
|
min_dbh
|
float
|
Smallest diameter (m). |
0.07
|
archetypes
|
An archetype name, or |
'broadleaf'
|
|
height_noise
|
float
|
Standard deviation of |
0.1
|
slope
|
float
|
Rise per metre (e.g. 0.1 is 5.7 degrees). |
0.1
|
aspect
|
float
|
Direction of steepest ascent (degrees clockwise from north, +y). |
90.0
|
roughness
|
float
|
Standard deviation of the micro-relief (m). |
0.05
|
roughness_length
|
float
|
Correlation length of the micro-relief (m). |
2.0
|
shrubs
|
float
|
Shrubs per hectare. |
400.0
|
grass_cover
|
float
|
Fraction of the plot covered by grass tufts. |
0.2
|
grass_height
|
float
|
Tallest grass (m). |
0.5
|
logs
|
float
|
Fallen logs and stumps per hectare. |
60.0
|
stumps
|
float
|
Fallen logs and stumps per hectare. |
60.0
|
ground_density
|
float
|
Terrain points per m², over the plot and |
400.0
|
margin
|
float
|
Terrain beyond the plot edge (m). |
2.0
|
point_density
|
float
|
Points per m² of wood, leaf, understorey and dead wood. |
1000.0
|
max_order
|
int
|
Highest branch order of the trees (1 to 4). |
3
|
lad
|
Leaf angle distribution of every tree, as in |
None
|
|
epicormic
|
float
|
Epicormic shoots per metre of bole on every tree. |
0.0
|
seed
|
int
|
Random seed. Trees are grown in parallel, each with its own seed, so the plot does not depend on the number of threads. |
0
|
Returns:
| Type | Description |
|---|---|
Plot
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For unknown names, values out of range, or more stems than fit in the plot without overlap. |
Examples:
>>> p = synthetic.plot(size=20, density=800, archetypes={"eucalypt": 2, "broadleaf": 1})
>>> p.trees["dbh"], p.stem_density, p.basal_area
>>> p.points.attrs["label"] # 1 ground ... 6 dead wood
scanner_preset ¶
scanner_preset(name: str) -> dict
Field of view and beam of a scanner model, as synthetic.scan
takes them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
One of |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown model. |