# Lichen mapping with Single Photon LiDAR (SPL)
Open-source Python workflow for lichen mapping facilitated by Single-Photon LiDAR (SPL).
Open-Source Code and Project Website:https://qianyorku.github.io/lichen-mapping-with-spl/.
## **Overview**
This repository provides a reproducible workflow for lichen mapping facilitated by Single Photon LiDAR (SPL) in boreal mixedwood forests.
It implements a modular Python pipeline for:
- Coordinate cleaning and consistent UTM projection for field plots
- Plot-level clipping of SPL canopy height models (CHM) and point clouds (LAZ)
- Individual Tree Crown (ITC) detection and delineation
- Plot-level LiDAR feature extraction from CHM and LAZ
- Lichen presence modeling with multiple classifiers (RF, Logit-EN, SVM-RBF)
The code is designed as an open, self-contained project guide so that researchers and practitioners can adapt the workflow to other regions and forest types.
---
## **Repository structure**
```text
lichen-mapping-with-spl/
├─ src/
│ ├─ verify_and_convert_coords_for_clipping.py
│ ├─ clip_plots_20m.py
│ ├─ itc_delineation_and_plot_features.py
│ ├─ plot_chm_laz_features.py
│ └─ lichen_models_rf_logit_svm.py
│
├─ data/
│ └─ demo_clipped_plots/
│ ├─ DEMO001_CHM_20m.tif
│ ├─ DEMO001_LAZ_20m.laz
│ └─ (optional: a few more tiny CHM/LAZ demo plots)
│
├─ outputs/
│ ├─ itc_delineation/ # ITC crowns & per-plot ITC metrics
│ ├─ plot_level_features/ # CHM/LAZ plot-level features
│ └─ lichen_models/ # model metrics, figures, tables
│
├─ README.md
├─ requirements.txt
├─ .gitignore
└─ LICENSE
All Python scripts are in src/:
verify_and_convert_coords_for_clipping.pyCleans and validates the plot coordinate table (CSV).
Uses latitude/longitude to:
Outputs:
clip_plots_20m.pyUses the cleaned coordinate CSV and the original SPL CHM/LAZ tiles.
For each plot, it:
Outputs:
VSNxxxxxx_CHM_20m.tif, VSNxxxxxx_LAZ_20m.laz)In this repository, you start from already clipped demo plots in data/demo_clipped_plots/.
itc_delineation_and_plot_features.pyBatch ITC detection and crown delineation when CHM and LAZ are in the same folder.
Automatically pairs files such as:
VSN601001_CHM_20m.tif ↔ VSN601001_LAZ_20m.laz (LAZ optional)ITC detection:
Crown delineation:
Outputs per plot:
*_tops.csv – treetop locations in map coordinates*_crowns_labels.tif – raster crown labels*_crowns.gpkg (optional) – crown polygons*_tree_table.csv – per-tree metrics (height, crown area, radius, edge/oversize flags, etc.)Outputs per batch:
master_itc_features.csv – slim plot-level ITC metrics (tree count, crown cover ratio, mean crown radius, etc.)plot_chm_laz_features.pyExtracts plot-level LiDAR features directly from CHM rasters and LAZ point clouds.
Uses filename patterns like:
PLOTID_CHM_20m.tifPLOTID_LAZ_20m.lazCHM features include:
LAZ features include:
Output:
outputs/plot_level_features/lichen_features.csv (default path)lichen_models_rf_logit_svm.pyBuilds and evaluates lichen presence models using:
Assumes the feature table has a binary column lichen_presence.
Workflow:
Outputs:
It is recommended to use conda or mamba:
conda create -n spl_lichen python=3.10
conda activate spl_lichen
From the repository root:
pip install -r requirements.txt
If you encounter issues with rasterio or geopandas, you may install them via conda first.
Small demo clipped plots (e.g., DEMO001_CHM_20m.tif, DEMO001_LAZ_20m.laz) are provided under:
data/demo_clipped_plots/
The full SPL tiles and the original plot coordinate CSV used in the real project are not included in this repository.
python src/itc_delineation_and_plot_features.py
By default, the script assumes:
DATA_DIR = "data/demo_clipped_plots"
OUT_ROOT = "outputs/itc_delineation"
Outputs will be written to outputs/itc_delineation/.
python src/plot_chm_laz_features.py
Default paths:
CLIPPED_DIR = Path("data/demo_clipped_plots")
OUTPUT_CSV = Path("outputs/plot_level_features/lichen_features.csv")
You can merge this table with your own lichen ground truth to add a lichen_presence column.
After adding a lichen_presence column to lichen_features.csv, run:
python src/lichen_models_rf_logit_svm.py
By default:
file_path = "outputs/plot_level_features/lichen_features.csv"
out_dir = "outputs/lichen_models"