Skip to main content

als-finder

A high-performance, cloud-native CLI engine for discovering, streaming, and partitioning raw LiDAR point cloud data across the globe.

Open in GitHub Codespaces

als-finder gathers complete acquisition footprints (project boundaries), true WGS84 point densities, and metadata from USGS 3DEP (EPT/COPC), NOAA Digital Coast, NASA G-LiHT, NEON, and OpenTopography into clean .json manifests, QGIS-ready .gpkg tables, and on-demand metric spatial tiles with automated Out-Of-Memory (OOM) protection.


🚀 Quick Install

Because als-finder uses advanced C++ geospatial libraries (GDAL, PDAL, GEOS), Conda or Docker is the recommended installation method:

# 1. Create environment with all C++ and Python dependencies
conda create -n als-finder -c conda-forge python=3.11 geopandas pdal python-pdal pystac stac-validator psutil shapely pyproj tqdm pyogrio requests click python-dotenv laspy -y

# 2. Activate environment & install als-finder
conda activate als-finder
pip install git+https://github.com/cms-2024-hudak/als-finder.git

🧭 The Big-Data Architecture: The 4-Stage ALS Lifecycle

Traditional tools force researchers to download massive, multi-gigabyte survey archives before doing any work. als-finder is built from the ground up for cloud-native lazy evaluation and HPC job arrays:

┌─────────────────┐     ┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│  1. DISCOVERY   │ ──> │   2. PLANNING    │ ──> │  3. ACQUISITION  │ ──> │   4. DERIVATIVE  │
│    (search)     │     │     (plan)       │     │     (fetch)      │     │    & MOSAIC      │
└─────────────────┘     └──────────────────┘     └──────────────────┘     └──────────────────┘
 • Federated APIs        • Spatial tiling index   • On-demand streaming    • Crop buffers
 • ROI & temporal        • Memory risk audit      • OOM auto-quadrants     • gdalbuildvrt
 • Provenance catalog    • Slurm task lists       • Sidecar crop bounds    • Zero edge-effects

⚡ Master Tutorial: End-to-End Walkthrough

This single, comprehensive tutorial walks through the complete lifecycle using a single workspace: scratch/test_workspace.

Step 0: Extract the Bundled Example Area of Interest (AOI)

als-finder comes with a bundled Lake Tahoe Basin Management Unit (LTBMU) boundary polygon so you can follow along immediately:

# Extract the example polygon (ltbmu_boundary.gpkg) to your current directory
als-finder get-example-roi

Search across all public LiDAR registries intersecting your AOI. Using --cloud-native filters exclusively for streaming-ready formats (USGS EPT / COPC), and --dedup eliminates duplicate survey records:

als-finder search \
  --roi ./ltbmu_boundary.gpkg \
  --workspace scratch/test_workspace \
  --cloud-native \
  --dedup

Console Output:

=================================================================================================================
 LiDAR Data Search Results 
=================================================================================================================
 | Provider        | Name                                   | Date         |   Est (GB) |   pts/m2 |   Area km2 |
-----------------------------------------------------------------------------------------------------------------
 | USGS_EPT        | CA_SierraNevada_5_2022                 | 2022-??-??   |    1380.20 |  29.1800 |    6347.47 |
 | USGS_EPT        | CA_SierraNevada_6_2022                 | 2022-??-??   |    1136.46 |  26.2100 |    5819.74 |
 | USGS_EPT        | CA_SierraNevada_8_2022                 | 2022-??-??   |    1171.62 |  25.1400 |    6255.39 |
 | NASA_GLIHT      | NASA G-LiHT Sierra Nevada / Lake Tahoe | 2022-07-18   |      30.73 |  32.5000 |     126.90 |
 | USGS_EPT        | NV_WestCentralEarthMRI_3_2020          | 2020-??-??   |     433.16 |   5.3400 |   10890.04 |
 | USGS_EPT        | CA_UpperSouthAmerican_Eldorado_2019    | 2019-??-??   |    2075.29 |  43.1600 |    6454.20 |
 | NOAA_STAC       | DigitalCoast_DAV:id_9452               | 2019-10-21   |    2075.29 |  46.7700 |    5954.91 |
 | USGS_EPT        | USGS_LPC_CA_NoCAL_Wildfires_B1_2018    | 2018-??-??   |     643.56 |  10.8900 |    7928.51 |
 | NOAA_STAC       | DigitalCoast_DAV:id_9036               | 2018-07-07   |     253.48 |   0.7800 |   43731.68 |
 | USGS_EPT        | USGS_LPC_NV_Reno_Carson_QL1_2017_LAS_2 | 2017-??-??   |     151.15 |   9.5400 |    2126.64 |
 | USGS_EPT        | CA_PlacerCo_2012                       | 2012-??-??   |      36.96 |   3.9500 |    1254.54 |
=================================================================================================================
 TOTAL DATASETS: 11 | ESTIMATED PAYLOAD: 9426.32 GB | QUERY TIME: 4.31s 
-----------------------------------------------------------------------------------------------------------------
 CATALOG TBL: scratch/test_workspace/catalog/catalog.gpkg
 JSON METADATA: scratch/test_workspace/catalog/manifest.json
=================================================================================================================

Step 2: Spatial Planning & Memory Risk Audit (als-finder plan)

Before launching jobs, use als-finder plan to inspect the study area's metric grid and audit memory safety against a target point budget (e.g. 4,000,000 points $\approx$ 4 GB RAM):

als-finder plan \
  --workspace scratch/test_workspace \
  --tile-size 500 \
  --buffer-size 50 \
  --max-points 4000000

Console Output:

==================================================
 ALS-FINDER SPATIAL PLANNING & GRID METRICS
==================================================
  Master Tiles:      5,657
  Total Leaf Tasks:  22,628
  Tile ID Range:     0 to 5656
  Tile Size:         500m (core)
  Buffer Size:       50m (overlap)
  Grid CRS:          EPSG:32610
  Target Tile ID:    0
  Basename:          CA_SierraNevada_5_2022_tile_E0737500_N4329500
  Hive Directory:    provider=USGS_EPT/dataset=CA_SierraNevada_5_2022/tilesize=500/buffer=50
  Crop PDAL Bounds:  ([737500.0, 738000.0], [4329000.0, 4329500.0])
  Crop GDAL -te:     737500.0 4329000.0 738000.0 4329500.0
  Grid File:         scratch/test_workspace/catalog/grids/tilesize=500/buffer=50/grid.gpkg
--------------------------------------------------
 MEMORY RISK AUDIT (PRE-FLIGHT):
  Max Points Budget: 4,000,000
  Est Points/Tile:   10,504,800
  Subdivision:       5657 tiles exceed budget (split into 4 quadrants each)
  Slurm Array Size:  --array=1-22628
==================================================

Extracting Fields Directly for Shell Scripts (--field and --format env):

# Query exact scalar fields:
als-finder plan --workspace scratch/test_workspace --tile-size 500 --buffer-size 50 --field total_tiles
# Output: 5657

# Export full environment variables into your active shell:
eval $(als-finder plan --workspace scratch/test_workspace --tile-id 15 --tile-size 500 --buffer-size 50 --format env)
echo "Target Basename: $BASENAME"
echo "Crop Bounds: $CROP_GDAL_TE"

Step 3: Generating HPC Slurm Task Lists & Manifests (--tasks / --tasks-csv)

When scheduling Slurm job arrays, you can output either a plain leaf tile list (--tasks) or a complete CSV manifest (--tasks-csv) with spatial coordinates, Hive paths, point counts, and recommended Slurm memory allocations:

# 1. Plain leaf task list for simple Slurm array indexes:
als-finder plan \
  --workspace scratch/test_workspace \
  --tasks > slurm_tasks.txt

# 2. Comprehensive CSV manifest for Slurm arrays, HPC job schedulers, and tracking:
als-finder plan \
  --workspace scratch/test_workspace \
  --tasks-csv slurm_manifest.csv

Manifest Columns in slurm_manifest.csv:

  • task_id: Unique leaf task identifier (0, 15_NW, etc.)
  • tile_id: Parent master tile integer
  • basename: Standardized spatial basename (<dataset>_tile_E<easting>_N<northing>[_quad])
  • hive_dir & hive_path: Deterministic Hive partition directory and destination file path
  • core_minx, core_miny, core_maxx, core_maxy: Core bounding coordinates
  • point_density, est_points: Acquisition point density ($pts/m^2$) and pre-flight point count
  • recommended_mem_gb: Pre-calculated Slurm job memory allocation ($\approx 1.5 \times$ point payload)

Step 4: On-Demand Point Cloud Streaming (als-finder fetch tile)

Acquire standard tile 15 directly over HTTP using clean positional syntax. Adding --sidecar writes a companion .json metadata file:

als-finder fetch tile 15 \
  --workspace scratch/test_workspace \
  --tile-size 500 \
  --buffer-size 50 \
  --sidecar \
  --overwrite

Console Output:

Successfully streamed tile 15 to scratch/test_workspace/data/tiles/provider=USGS_EPT/dataset=CA_SierraNevada_5_2022/tilesize=500/buffer=50/CA_SierraNevada_5_2022_tile_E0738000_N4331000.laz
Wrote metadata sidecar to scratch/test_workspace/data/tiles/provider=USGS_EPT/dataset=CA_SierraNevada_5_2022/tilesize=500/buffer=50/CA_SierraNevada_5_2022_tile_E0738000_N4331000.json

Step 5: Automatic OOM Protection & Quadrant Streaming

If a tile is hyper-dense and would exceed your RAM limit, passing --max-points causes als-finder to automatically bisect it into 4 safe quadrants:

als-finder fetch tile 15 \
  --workspace scratch/test_workspace \
  --tile-size 500 \
  --buffer-size 50 \
  --max-points 4000000 \
  --sidecar \
  --overwrite

Console Output:

Tile 15 exceeded budget. Subdivided into 4 quadrants:
  - Streamed 15_NW to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_NW.laz
  - Streamed 15_NE to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_NE.laz
  - Streamed 15_SW to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_SW.laz
  - Streamed 15_SE to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_SE.laz

Step 6: Multi-Tile Batch Streaming

Stream multiple tiles in a single concise command:

als-finder fetch tile 14 16 \
  --workspace scratch/test_workspace \
  --tile-size 500 \
  --buffer-size 50 \
  --sidecar \
  --overwrite

Console Output:

Successfully processed 2 target tile(s) (2 file(s) generated):
  - Tile 14 -> .../CA_SierraNevada_5_2022_tile_E0738000_N4330500.laz
  - Tile 16 -> .../CA_SierraNevada_5_2022_tile_E0738000_N4331500.laz

Step 7: Downstream Unbuffering & 1-Line Mosaicking

Because every .laz file includes a companion .json sidecar storing exact unbuffering crop parameters (crop_gdal_te / crop_pdal_bounds), downstream processing and mosaicking are completely automated:

TILES_DIR="scratch/test_workspace/data/tiles/provider=USGS_EPT/dataset=CA_SierraNevada_5_2022/tilesize=500/buffer=50"

# 1. Process point clouds and crop away the 50m buffer
for LAZ in "$TILES_DIR"/*.laz; do
    SIDECAR="${LAZ%.laz}.json"
    CROP_BOX=$(jq -r '.crop_gdal_te' "$SIDECAR")

    # Example: compute Canopy Height Model (CHM) with buffer, then crop to core:
    # my_chm_tool "$LAZ" temp.tif
    # gdal_translate -projwin $CROP_BOX temp.tif "${LAZ%.laz}_chm.tif"
done

# 2. Build a seamless mosaic across the entire study area (works for both 500m and 250m tiles):
# gdalbuildvrt full_study_area_chm.vrt "$TILES_DIR"/*_chm.tif

Native lidR (R) Integration & Buffer Point Tagging

Streamed .laz tiles embed a native LAS 1.4 buffer extra dimension (buffer=0 for points inside the core tile, buffer=1 for boundary buffer overlap points). In R using lidR, you can filter away buffer points after edge-artifact-free metric calculation with zero geometry math:

library(lidR)

# Read LAS tile with extra dimensions:
las <- readLAS("tile.laz", select = "xyzc", filter = "")

# Compute metrics (e.g., CHM, canopy cover) with buffer points preventing edge voids:
# ...

# Instant zero-copy filter of core points only:
core_pts <- filter_poi(las, buffer == 0)

Step 8: High-Throughput Slurm Job Array Recipe

Here is the production Slurm script for running on-demand streaming across hundreds of HPC cores with zero OOM risk and instant scratch cleanup:

#!/usr/bin/env bash
#SBATCH --job-name=als_tiles
#SBATCH --array=0-5656
#SBATCH --cpus-per-task=1
#SBATCH --mem=4G
#SBATCH --time=00:30:00

set -e

WORKSPACE="/shared/project/workspace"
FINAL_DIR="/shared/project/products/chm"
LOCAL_SCRATCH="$TMPDIR/als_work_${SLURM_ARRAY_TASK_ID}"
mkdir -p "$LOCAL_SCRATCH" "$FINAL_DIR"

# 1. Fetch on demand to local scratch (auto-subdivides if dense)
als-finder fetch tile "$SLURM_ARRAY_TASK_ID" \
    --workspace "$WORKSPACE" \
    --tile-size 500 \
    --buffer-size 50 \
    --max-points 4000000 \
    --output "$LOCAL_SCRATCH" \
    --sidecar \
    --overwrite

# 2. Process all files in scratch (whether 1 tile or 4 quadrants)
for LAZ in "$LOCAL_SCRATCH"/*.laz; do
    SIDECAR="${LAZ%.laz}.json"
    BASENAME=$(basename "$LAZ" .laz)
    CROP_BOX=$(jq -r '.crop_gdal_te' "$SIDECAR")

    # Run point cloud metric tool
    # compute_metric "$LAZ" "$LOCAL_SCRATCH/${BASENAME}_raw.tif"

    # Crop off buffer to permanent storage
    # gdal_translate -projwin $CROP_BOX "$LOCAL_SCRATCH/${BASENAME}_raw.tif" "$FINAL_DIR/${BASENAME}_chm.tif"

    # Immediate cleanup of raw point cloud
    rm -f "$LAZ" "$SIDECAR"
done

# Clean scratch
rm -rf "$LOCAL_SCRATCH"

Step 9: Bulk Offline Survey Archiving (als-finder fetch survey)

For researchers who prefer to pre-download the entire raw dataset locally for air-gapped or offline processing:

als-finder fetch survey \
  --workspace scratch/test_workspace \
  --name "CA_SierraNevada_5_2022" \
  --execute

Step 10: Workspace Maintenance (als-finder workspace)

Update catalog filters or clean interim scratch files:

# Update catalog criteria (e.g. restrict to QL1):
als-finder workspace update --workspace scratch/test_workspace --density QL1

# Clean scratch workspace:
als-finder workspace clean --workspace scratch/test_workspace

🐍 Programmatic Python API

You can also import and use als_finder directly in Python scripts and Jupyter Notebooks:

import als_finder

# 1. Query tile specification and unbuffering bounds
spec = als_finder.get_tile_spec(
    workspace="scratch/test_workspace",
    tile_id=15,
    tile_size=500,
    buffer_size=50
)

print(f"Basename: {spec['basename']}")
print(f"Grid CRS: {spec['grid_crs']}")
print(f"Crop GDAL -te: {spec['crop_gdal_te']}")

# 2. Inspect Central Core Area vs Buffered Footprint
core = spec['core_poly']
buffered = spec['buffered_poly']

print(f"Core Tile Dimensions: {round(core.bounds[2] - core.bounds[0])}m x {round(core.bounds[3] - core.bounds[1])}m")
print(f"Buffer Extension: {round((buffered.bounds[2] - core.bounds[2]))}m on all sides")

🏛️ Authorship & Citation

This software is released under the open-source MIT License. Copyright Jonathan Greenberg.

Project Authors & Contributors:

  • Jonathan Greenberg (University of Nevada, Reno): Lead Developer and Core Project Architect.
  • Andrew Hudak (US Forest Service): Advisory feedback and scientific computing grant alignment.
  • Antigravity (Google DeepMind): AI Software Engineering Partner.

Metadata

Release files for als-finder 0.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for als-finder 0.0.0
File Size Uploaded
als_finder-0.0.0.tar.gz 96.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for als-finder 0.0.0
File Interpreter ABI Platform
als_finder-0.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 192.2 kB

Release files / als_finder-0.0.0.tar.gz

Download URL als_finder-0.0.0.tar.gz
Size 96.1 kB
Tags Source
SHA-256 checksum
How to use checksums
57fab4c805959e56810fdf67781e333120b132bdc4e94f76cc8023ac61f3ebd3
BLAKE2b-256 checksum
How to use checksums
2049fc08be43eea6d25e2e25f8efdacfb167a78ff161626ac05773192b3af4d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / als_finder-0.0.0-py3-none-any.whl

Download URL als_finder-0.0.0-py3-none-any.whl
Size 96.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bac0de98837652a79a70ae143d13cf8f0dd4a0f5df17c134b6ebb0f9c25ca730
BLAKE2b-256 checksum
How to use checksums
441c445eed9cba4d7c0d4e70cf879ee47a260dbd69bbf2cdc6d4b95322bb87da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

1.3.1

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.2

2 release files

1.0.1

2 release files

This release

0.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page