ForestStructure calculates fixed-tile forest metrics from one LAS/LAZ point cloud and an optional audited area of interest. It produces tile metrics, segment diagnostics, vector tiles, a DTM, a CHM, and a tile overview.
The recommended workflow uses a canonical COPC for spatial streaming while
preserving the scientific behavior of the ordered source LAZ. See
COPC_REVIEW_GUIDE.md for the design, failure fixes,
and validation evidence.
The unchanged scientific reference is
reference/Indices_Final_run.R, SHA-256
746d57b4c937001af31e4ccd1b9f14edb5cebb15d46154ad9e20d0ce39f78226.
DTM generation first uses lidR's optimized integer TIN. If that implementation rejects coordinates that do not match the LAS scale/offset integer grid, the same chunk is retried with lidR 4.3.2's legacy floating-point TIN. The fallback does not rescale coordinates or modify the LAS header.
A real replay of dataset 2153 (8101.laz, 40,453,840 points) reproduced the
historical integer-conversion failure with the old image and completed with the
legacy retry under 10 CPUs and a 30 GiB limit. The accepted run used 6.25 GiB
peak cgroup memory and produced all 12 expected artifacts. This proves failure
recovery, but not zero-tolerance valid_updated parity: dataset 2153 has no
confirmed oracle in the validation cohort, and comparison with its older
generic-COPC result found small raster and segment differences. These small
changes are expected when the fallback triangulates the original floating-point
XY coordinates instead of temporarily changing their scale for the integer TIN.
Keeping the source coordinates and LAS header unchanged is scientifically more
faithful to the measured point cloud. See
COPC_REVIEW_GUIDE.md and the machine-readable
validation record in benchmarks/results/ before promoting this retry strategy.
Build the base image and the ordered-COPC image:
make build IMAGE=3dtrees-foreststructure:v0.1.2
make build-julia-memory-safe \
JULIA_MEMORY_SAFE_IMAGE=3dtrees-foreststructure:copc-localDo not use an arbitrary COPC. Build it from the ordered LAS/LAZ so it contains
the validated OriginalPointIndex needed to restore source record order:
FORESTSTRUCTURE_JULIA_IMAGE=3dtrees-foreststructure:copc-local \
FORESTSTRUCTURE_TEST_CPUS=10 \
FORESTSTRUCTURE_TEST_MEMORY_GIB=30 \
bash tests/build_original_order_copc.sh \
/data/original.laz \
/data/original.copc.laz \
/data/original.copc.order.jsonThe builder preserves all point values, places the supported instance dimensions in selectively readable positions, validates every source index, and publishes the COPC only after validation succeeds.
The validated controlled profile is 10 CPUs, a 30 GiB hard memory limit, a 25 GiB internal budget, and one LAScatalog worker:
docker run --rm --network none \
--cpus 10 \
--memory 30g \
--memory-swap 30g \
--user "$(id -u):$(id -g)" \
--env FORESTSTRUCTURE_THREADS=10 \
--env FORESTSTRUCTURE_CATALOG_WORKERS=1 \
--volume /local/input:/in:ro \
--volume /local/output:/out \
--volume /local/work:/work \
3dtrees-foreststructure:copc-local \
--point-cloud /in/original.copc.laz \
--original-point-cloud /in/original.laz \
--aoi-geojson /in/aoi.geojson \
--dataset-id 150 \
--output-dir /out \
--temp-dir /work \
--memory-budget-gib 25--original-point-cloud is optional. When supplied, it is used only for
header identity and provenance checks; scientific point reads still use COPC.
Plain LAS/LAZ input is also supported directly.
Supported instance dimensions are:
PredInstancePredInstance_SATPredInstance_FM
Missing dimensions are skipped. Available dimensions are processed independently so their segment IDs and results cannot mix.
The AOI is interpreted in the point cloud's local XY coordinate space and is not reprojected. Without an AOI, the full point-cloud extent is tiled.
For each available instance dimension, the run writes:
<dataset>_<dimension>_results.csv<dataset>_<dimension>_segment_diagnostics.csv<dataset>_<dimension>_tiles.geojson
Shared outputs are:
<dataset>_dtm.tif<dataset>_chm.tif<dataset>_tiles.png<dataset>_aoi_conversion.json<dataset>_julia_memory_safe_run.json<dataset>_performance.csv
Outputs are computed in a staging directory, validated as a complete set, and then promoted. A failed run does not publish a partial result set.
Run the core container tests:
make test
make test-julia-memory-safeVerify ordered LAZ and COPC equality for all three instance dimensions:
make test-copc-all-instance-dimensionsValidate one real dataset against an existing oracle:
make test-valid-updated-copc-alignment \
DATASET_ID=150 \
COPC_LAZ=/path/to/input.copc.laz \
ORIGINAL_LAZ=/path/to/original.laz \
AOI_GEOJSON=/path/to/aoi.geojson \
VALID_UPDATED_DIR=/path/to/oracle \
INSTANCE_DIMENSION=PredInstanceThe acceptance comparison requires zero differences in result CSVs, segment diagnostics, tile GeoJSON, DTM cells, and CHM cells.
src/— analysis and container entrypoint codetests/— conversion, regression, and differential validationreference/— unchanged scientific reference scriptbenchmarks/— benchmark scripts and compact result dataDockerfile.julia-memory-safe— ordered-COPC imageCOPC_REVIEW_GUIDE.md— detailed implementation explanation