als-finder 0.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. als_finder-0.0.0/LICENSE +21 -0
  2. als_finder-0.0.0/MANIFEST.in +4 -0
  3. als_finder-0.0.0/PKG-INFO +447 -0
  4. als_finder-0.0.0/README.md +403 -0
  5. als_finder-0.0.0/setup.cfg +4 -0
  6. als_finder-0.0.0/setup.py +47 -0
  7. als_finder-0.0.0/src/als_finder/__init__.py +38 -0
  8. als_finder-0.0.0/src/als_finder/__main__.py +0 -0
  9. als_finder-0.0.0/src/als_finder/cli.py +1856 -0
  10. als_finder-0.0.0/src/als_finder/core/__init__.py +0 -0
  11. als_finder-0.0.0/src/als_finder/core/auth_manager.py +93 -0
  12. als_finder-0.0.0/src/als_finder/core/grid_manager.py +833 -0
  13. als_finder-0.0.0/src/als_finder/core/input_manager.py +95 -0
  14. als_finder-0.0.0/src/als_finder/core/local_catalog.py +101 -0
  15. als_finder-0.0.0/src/als_finder/core/quicklooks.py +219 -0
  16. als_finder-0.0.0/src/als_finder/core/stac_generator.py +163 -0
  17. als_finder-0.0.0/src/als_finder/core/standardization.py +765 -0
  18. als_finder-0.0.0/src/als_finder/data/__init__.py +0 -0
  19. als_finder-0.0.0/src/als_finder/data/ltbmu_boundary.gpkg +0 -0
  20. als_finder-0.0.0/src/als_finder/download.py +363 -0
  21. als_finder-0.0.0/src/als_finder/providers/__init__.py +111 -0
  22. als_finder-0.0.0/src/als_finder/providers/base.py +99 -0
  23. als_finder-0.0.0/src/als_finder/providers/earthdata.py +195 -0
  24. als_finder-0.0.0/src/als_finder/providers/gliht.py +237 -0
  25. als_finder-0.0.0/src/als_finder/providers/neon.py +257 -0
  26. als_finder-0.0.0/src/als_finder/providers/noaa.py +308 -0
  27. als_finder-0.0.0/src/als_finder/providers/opentopography.py +336 -0
  28. als_finder-0.0.0/src/als_finder/providers/usgs.py +218 -0
  29. als_finder-0.0.0/src/als_finder.egg-info/PKG-INFO +447 -0
  30. als_finder-0.0.0/src/als_finder.egg-info/SOURCES.txt +38 -0
  31. als_finder-0.0.0/src/als_finder.egg-info/dependency_links.txt +1 -0
  32. als_finder-0.0.0/src/als_finder.egg-info/entry_points.txt +2 -0
  33. als_finder-0.0.0/src/als_finder.egg-info/requires.txt +22 -0
  34. als_finder-0.0.0/src/als_finder.egg-info/top_level.txt +1 -0
  35. als_finder-0.0.0/tests/test_cli_tile.py +151 -0
  36. als_finder-0.0.0/tests/test_grid_manager.py +108 -0
  37. als_finder-0.0.0/tests/test_hierarchical_grid.py +162 -0
  38. als_finder-0.0.0/tests/test_provider_earthdata.py +48 -0
  39. als_finder-0.0.0/tests/test_provider_gliht.py +93 -0
  40. als_finder-0.0.0/tests/test_provider_neon.py +61 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonathan Greenberg / University of Nevada, Reno
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include examples *
4
+ recursive-include src/als_finder/data *
@@ -0,0 +1,447 @@
1
+ Metadata-Version: 2.4
2
+ Name: als-finder
3
+ Version: 0.0.0
4
+ Summary: A high-performance, cloud-native CLI engine for discovering and parsing raw LiDAR point cloud metadata.
5
+ Home-page: https://github.com/cms-2024-hudak/als-finder
6
+ Author: Jonathan Greenberg
7
+ Author-email: jgreenberg@unr.edu
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: click
15
+ Requires-Dist: requests
16
+ Requires-Dist: geopandas
17
+ Requires-Dist: shapely
18
+ Requires-Dist: pyproj
19
+ Requires-Dist: python-dotenv
20
+ Requires-Dist: tqdm
21
+ Requires-Dist: pyogrio
22
+ Requires-Dist: pystac
23
+ Requires-Dist: stac-validator
24
+ Requires-Dist: psutil
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest; extra == "dev"
27
+ Requires-Dist: setuptools_scm; extra == "dev"
28
+ Requires-Dist: build; extra == "dev"
29
+ Provides-Extra: pdal
30
+ Requires-Dist: pdal; extra == "pdal"
31
+ Provides-Extra: all
32
+ Requires-Dist: pdal; extra == "all"
33
+ Dynamic: author
34
+ Dynamic: author-email
35
+ Dynamic: classifier
36
+ Dynamic: description
37
+ Dynamic: description-content-type
38
+ Dynamic: home-page
39
+ Dynamic: license-file
40
+ Dynamic: provides-extra
41
+ Dynamic: requires-dist
42
+ Dynamic: requires-python
43
+ Dynamic: summary
44
+
45
+ # als-finder
46
+
47
+ **A high-performance, cloud-native CLI engine for discovering, streaming, and partitioning raw LiDAR point cloud data across the globe.**
48
+
49
+ [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/cms-2024-hudak/als-finder)
50
+
51
+ `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.
52
+
53
+ ---
54
+
55
+ ## 🚀 Quick Install
56
+
57
+ Because `als-finder` uses advanced C++ geospatial libraries (GDAL, PDAL, GEOS), **Conda** or **Docker** is the recommended installation method:
58
+
59
+ ```bash
60
+ # 1. Create environment with all C++ and Python dependencies
61
+ 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
62
+
63
+ # 2. Activate environment & install als-finder
64
+ conda activate als-finder
65
+ pip install git+https://github.com/cms-2024-hudak/als-finder.git
66
+ ```
67
+
68
+ > [!TIP]
69
+ > 📖 **Full Installation Guide:** See [**docs/INSTALLATION.md**](docs/INSTALLATION.md) for Conda-Forge releases, Docker container runs, Singularity/Apptainer HPC setup, and Windows/WSL2 instructions.
70
+
71
+ ---
72
+
73
+ ## 🧭 The Big-Data Architecture: The 4-Stage ALS Lifecycle
74
+
75
+ 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**:
76
+
77
+ ```
78
+ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
79
+ │ 1. DISCOVERY │ ──> │ 2. PLANNING │ ──> │ 3. ACQUISITION │ ──> │ 4. DERIVATIVE │
80
+ │ (search) │ │ (plan) │ │ (fetch) │ │ & MOSAIC │
81
+ └─────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘
82
+ • Federated APIs • Spatial tiling index • On-demand streaming • Crop buffers
83
+ • ROI & temporal • Memory risk audit • OOM auto-quadrants • gdalbuildvrt
84
+ • Provenance catalog • Slurm task lists • Sidecar crop bounds • Zero edge-effects
85
+ ```
86
+
87
+ ---
88
+
89
+ ## ⚡ Master Tutorial: End-to-End Walkthrough
90
+
91
+ This single, comprehensive tutorial walks through the complete lifecycle using a single workspace: `scratch/test_workspace`.
92
+
93
+ ### Step 0: Extract the Bundled Example Area of Interest (AOI)
94
+ `als-finder` comes with a bundled Lake Tahoe Basin Management Unit (LTBMU) boundary polygon so you can follow along immediately:
95
+
96
+ ```bash
97
+ # Extract the example polygon (ltbmu_boundary.gpkg) to your current directory
98
+ als-finder get-example-roi
99
+ ```
100
+
101
+ ---
102
+
103
+ ### Step 1: Federated Discovery (`als-finder search`)
104
+ 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:
105
+
106
+ ```bash
107
+ als-finder search \
108
+ --roi ./ltbmu_boundary.gpkg \
109
+ --workspace scratch/test_workspace \
110
+ --cloud-native \
111
+ --dedup
112
+ ```
113
+
114
+ **Console Output:**
115
+ ```text
116
+ =================================================================================================================
117
+ LiDAR Data Search Results
118
+ =================================================================================================================
119
+ | Provider | Name | Date | Est (GB) | pts/m2 | Area km2 |
120
+ -----------------------------------------------------------------------------------------------------------------
121
+ | USGS_EPT | CA_SierraNevada_5_2022 | 2022-??-?? | 1380.20 | 29.1800 | 6347.47 |
122
+ | USGS_EPT | CA_SierraNevada_6_2022 | 2022-??-?? | 1136.46 | 26.2100 | 5819.74 |
123
+ | USGS_EPT | CA_SierraNevada_8_2022 | 2022-??-?? | 1171.62 | 25.1400 | 6255.39 |
124
+ | NASA_GLIHT | NASA G-LiHT Sierra Nevada / Lake Tahoe | 2022-07-18 | 30.73 | 32.5000 | 126.90 |
125
+ | USGS_EPT | NV_WestCentralEarthMRI_3_2020 | 2020-??-?? | 433.16 | 5.3400 | 10890.04 |
126
+ | USGS_EPT | CA_UpperSouthAmerican_Eldorado_2019 | 2019-??-?? | 2075.29 | 43.1600 | 6454.20 |
127
+ | NOAA_STAC | DigitalCoast_DAV:id_9452 | 2019-10-21 | 2075.29 | 46.7700 | 5954.91 |
128
+ | USGS_EPT | USGS_LPC_CA_NoCAL_Wildfires_B1_2018 | 2018-??-?? | 643.56 | 10.8900 | 7928.51 |
129
+ | NOAA_STAC | DigitalCoast_DAV:id_9036 | 2018-07-07 | 253.48 | 0.7800 | 43731.68 |
130
+ | USGS_EPT | USGS_LPC_NV_Reno_Carson_QL1_2017_LAS_2 | 2017-??-?? | 151.15 | 9.5400 | 2126.64 |
131
+ | USGS_EPT | CA_PlacerCo_2012 | 2012-??-?? | 36.96 | 3.9500 | 1254.54 |
132
+ =================================================================================================================
133
+ TOTAL DATASETS: 11 | ESTIMATED PAYLOAD: 9426.32 GB | QUERY TIME: 4.31s
134
+ -----------------------------------------------------------------------------------------------------------------
135
+ CATALOG TBL: scratch/test_workspace/catalog/catalog.gpkg
136
+ JSON METADATA: scratch/test_workspace/catalog/manifest.json
137
+ =================================================================================================================
138
+ ```
139
+
140
+ > [!NOTE]
141
+ > **Zero Heavy Downloads:** Notice that 9.4 TB of data was discovered in 4 seconds. No heavy point cloud files were downloaded to your hard drive.
142
+
143
+ ---
144
+
145
+ ### Step 2: Spatial Planning & Memory Risk Audit (`als-finder plan`)
146
+ 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):
147
+
148
+ ```bash
149
+ als-finder plan \
150
+ --workspace scratch/test_workspace \
151
+ --tile-size 500 \
152
+ --buffer-size 50 \
153
+ --max-points 4000000
154
+ ```
155
+
156
+ **Console Output:**
157
+ ```text
158
+ ==================================================
159
+ ALS-FINDER SPATIAL PLANNING & GRID METRICS
160
+ ==================================================
161
+ Master Tiles: 5,657
162
+ Total Leaf Tasks: 22,628
163
+ Tile ID Range: 0 to 5656
164
+ Tile Size: 500m (core)
165
+ Buffer Size: 50m (overlap)
166
+ Grid CRS: EPSG:32610
167
+ Target Tile ID: 0
168
+ Basename: CA_SierraNevada_5_2022_tile_E0737500_N4329500
169
+ Hive Directory: provider=USGS_EPT/dataset=CA_SierraNevada_5_2022/tilesize=500/buffer=50
170
+ Crop PDAL Bounds: ([737500.0, 738000.0], [4329000.0, 4329500.0])
171
+ Crop GDAL -te: 737500.0 4329000.0 738000.0 4329500.0
172
+ Grid File: scratch/test_workspace/catalog/grids/tilesize=500/buffer=50/grid.gpkg
173
+ --------------------------------------------------
174
+ MEMORY RISK AUDIT (PRE-FLIGHT):
175
+ Max Points Budget: 4,000,000
176
+ Est Points/Tile: 10,504,800
177
+ Subdivision: 5657 tiles exceed budget (split into 4 quadrants each)
178
+ Slurm Array Size: --array=1-22628
179
+ ==================================================
180
+ ```
181
+
182
+ #### Extracting Fields Directly for Shell Scripts (`--field` and `--format env`):
183
+ ```bash
184
+ # Query exact scalar fields:
185
+ als-finder plan --workspace scratch/test_workspace --tile-size 500 --buffer-size 50 --field total_tiles
186
+ # Output: 5657
187
+
188
+ # Export full environment variables into your active shell:
189
+ eval $(als-finder plan --workspace scratch/test_workspace --tile-id 15 --tile-size 500 --buffer-size 50 --format env)
190
+ echo "Target Basename: $BASENAME"
191
+ echo "Crop Bounds: $CROP_GDAL_TE"
192
+ ```
193
+
194
+ > [!TIP]
195
+ > **Standardized Grid Alignment (1200m Core / 30m Buffer):**
196
+ > By default, `als-finder` generates $1200\,\text{m}$ core tiles with a $30\,\text{m}$ buffer. This geometry guarantees exact pixel integer divisibility across standard remote sensing raster resolutions—$30\,\text{m}$ ($40\,\text{px}$ core, $42\,\text{px}$ buffered), $10\,\text{m}$ ($120\,\text{px}$), and $1\,\text{m}$ ($1200\,\text{px}$)—completely preventing sub-pixel edge interpolation artifacts.
197
+
198
+ ---
199
+
200
+ ### Step 3: Generating HPC Slurm Task Lists & Manifests (`--tasks` / `--tasks-csv`)
201
+ 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:
202
+
203
+ ```bash
204
+ # 1. Plain leaf task list for simple Slurm array indexes:
205
+ als-finder plan \
206
+ --workspace scratch/test_workspace \
207
+ --tasks > slurm_tasks.txt
208
+
209
+ # 2. Comprehensive CSV manifest for Slurm arrays, HPC job schedulers, and tracking:
210
+ als-finder plan \
211
+ --workspace scratch/test_workspace \
212
+ --tasks-csv slurm_manifest.csv
213
+ ```
214
+
215
+ **Manifest Columns in `slurm_manifest.csv`:**
216
+ - `task_id`: Unique leaf task identifier (`0`, `15_NW`, etc.)
217
+ - `tile_id`: Parent master tile integer
218
+ - `basename`: Standardized spatial basename (`<dataset>_tile_E<easting>_N<northing>[_quad]`)
219
+ - `hive_dir` & `hive_path`: Deterministic Hive partition directory and destination file path
220
+ - `core_minx`, `core_miny`, `core_maxx`, `core_maxy`: Core bounding coordinates
221
+ - `point_density`, `est_points`: Acquisition point density ($pts/m^2$) and pre-flight point count
222
+ - `recommended_mem_gb`: Pre-calculated Slurm job memory allocation ($\approx 1.5 \times$ point payload)
223
+
224
+ ---
225
+
226
+ ### Step 4: On-Demand Point Cloud Streaming (`als-finder fetch tile`)
227
+ Acquire standard tile 15 directly over HTTP using clean positional syntax. Adding `--sidecar` writes a companion `.json` metadata file:
228
+
229
+ ```bash
230
+ als-finder fetch tile 15 \
231
+ --workspace scratch/test_workspace \
232
+ --tile-size 500 \
233
+ --buffer-size 50 \
234
+ --sidecar \
235
+ --overwrite
236
+ ```
237
+
238
+ **Console Output:**
239
+ ```text
240
+ 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
241
+ 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
242
+ ```
243
+
244
+ ---
245
+
246
+ ### Step 5: Automatic OOM Protection & Quadrant Streaming
247
+ 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:
248
+
249
+ ```bash
250
+ als-finder fetch tile 15 \
251
+ --workspace scratch/test_workspace \
252
+ --tile-size 500 \
253
+ --buffer-size 50 \
254
+ --max-points 4000000 \
255
+ --sidecar \
256
+ --overwrite
257
+ ```
258
+
259
+ **Console Output:**
260
+ ```text
261
+ Tile 15 exceeded budget. Subdivided into 4 quadrants:
262
+ - Streamed 15_NW to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_NW.laz
263
+ - Streamed 15_NE to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_NE.laz
264
+ - Streamed 15_SW to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_SW.laz
265
+ - Streamed 15_SE to .../CA_SierraNevada_5_2022_tile_E0738000_N4331000_SE.laz
266
+ ```
267
+
268
+ > [!IMPORTANT]
269
+ > **Co-Located Subtiles & Constant Buffer:**
270
+ > 1. All quadrants sit directly in the parent Hive directory (`tilesize=500/buffer=50`), eliminating nested directory crawling.
271
+ > 2. The 50m buffer is **fully preserved** across all subdivisions, preventing edge-effect interpolation degradation.
272
+
273
+ ---
274
+
275
+ ### Step 6: Multi-Tile Batch Streaming
276
+ Stream multiple tiles in a single concise command:
277
+
278
+ ```bash
279
+ als-finder fetch tile 14 16 \
280
+ --workspace scratch/test_workspace \
281
+ --tile-size 500 \
282
+ --buffer-size 50 \
283
+ --sidecar \
284
+ --overwrite
285
+ ```
286
+
287
+ **Console Output:**
288
+ ```text
289
+ Successfully processed 2 target tile(s) (2 file(s) generated):
290
+ - Tile 14 -> .../CA_SierraNevada_5_2022_tile_E0738000_N4330500.laz
291
+ - Tile 16 -> .../CA_SierraNevada_5_2022_tile_E0738000_N4331500.laz
292
+ ```
293
+
294
+ ---
295
+
296
+ ### Step 7: Downstream Unbuffering & 1-Line Mosaicking
297
+
298
+ 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:
299
+
300
+ ```bash
301
+ TILES_DIR="scratch/test_workspace/data/tiles/provider=USGS_EPT/dataset=CA_SierraNevada_5_2022/tilesize=500/buffer=50"
302
+
303
+ # 1. Process point clouds and crop away the 50m buffer
304
+ for LAZ in "$TILES_DIR"/*.laz; do
305
+ SIDECAR="${LAZ%.laz}.json"
306
+ CROP_BOX=$(jq -r '.crop_gdal_te' "$SIDECAR")
307
+
308
+ # Example: compute Canopy Height Model (CHM) with buffer, then crop to core:
309
+ # my_chm_tool "$LAZ" temp.tif
310
+ # gdal_translate -projwin $CROP_BOX temp.tif "${LAZ%.laz}_chm.tif"
311
+ done
312
+
313
+ # 2. Build a seamless mosaic across the entire study area (works for both 500m and 250m tiles):
314
+ # gdalbuildvrt full_study_area_chm.vrt "$TILES_DIR"/*_chm.tif
315
+ ```
316
+
317
+ #### Native lidR (R) Integration & Buffer Point Tagging
318
+ 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:
319
+
320
+ ```r
321
+ library(lidR)
322
+
323
+ # Read LAS tile with extra dimensions:
324
+ las <- readLAS("tile.laz", select = "xyzc", filter = "")
325
+
326
+ # Compute metrics (e.g., CHM, canopy cover) with buffer points preventing edge voids:
327
+ # ...
328
+
329
+ # Instant zero-copy filter of core points only:
330
+ core_pts <- filter_poi(las, buffer == 0)
331
+ ```
332
+
333
+ ---
334
+
335
+ ### Step 8: High-Throughput Slurm Job Array Recipe
336
+
337
+ Here is the production Slurm script for running on-demand streaming across hundreds of HPC cores with zero OOM risk and instant scratch cleanup:
338
+
339
+ ```bash
340
+ #!/usr/bin/env bash
341
+ #SBATCH --job-name=als_tiles
342
+ #SBATCH --array=0-5656
343
+ #SBATCH --cpus-per-task=1
344
+ #SBATCH --mem=4G
345
+ #SBATCH --time=00:30:00
346
+
347
+ set -e
348
+
349
+ WORKSPACE="/shared/project/workspace"
350
+ FINAL_DIR="/shared/project/products/chm"
351
+ LOCAL_SCRATCH="$TMPDIR/als_work_${SLURM_ARRAY_TASK_ID}"
352
+ mkdir -p "$LOCAL_SCRATCH" "$FINAL_DIR"
353
+
354
+ # 1. Fetch on demand to local scratch (auto-subdivides if dense)
355
+ als-finder fetch tile "$SLURM_ARRAY_TASK_ID" \
356
+ --workspace "$WORKSPACE" \
357
+ --tile-size 500 \
358
+ --buffer-size 50 \
359
+ --max-points 4000000 \
360
+ --output "$LOCAL_SCRATCH" \
361
+ --sidecar \
362
+ --overwrite
363
+
364
+ # 2. Process all files in scratch (whether 1 tile or 4 quadrants)
365
+ for LAZ in "$LOCAL_SCRATCH"/*.laz; do
366
+ SIDECAR="${LAZ%.laz}.json"
367
+ BASENAME=$(basename "$LAZ" .laz)
368
+ CROP_BOX=$(jq -r '.crop_gdal_te' "$SIDECAR")
369
+
370
+ # Run point cloud metric tool
371
+ # compute_metric "$LAZ" "$LOCAL_SCRATCH/${BASENAME}_raw.tif"
372
+
373
+ # Crop off buffer to permanent storage
374
+ # gdal_translate -projwin $CROP_BOX "$LOCAL_SCRATCH/${BASENAME}_raw.tif" "$FINAL_DIR/${BASENAME}_chm.tif"
375
+
376
+ # Immediate cleanup of raw point cloud
377
+ rm -f "$LAZ" "$SIDECAR"
378
+ done
379
+
380
+ # Clean scratch
381
+ rm -rf "$LOCAL_SCRATCH"
382
+ ```
383
+
384
+ ---
385
+
386
+ ### Step 9: Bulk Offline Survey Archiving (`als-finder fetch survey`)
387
+ For researchers who prefer to pre-download the entire raw dataset locally for air-gapped or offline processing:
388
+
389
+ ```bash
390
+ als-finder fetch survey \
391
+ --workspace scratch/test_workspace \
392
+ --name "CA_SierraNevada_5_2022" \
393
+ --execute
394
+ ```
395
+
396
+ ---
397
+
398
+ ### Step 10: Workspace Maintenance (`als-finder workspace`)
399
+ Update catalog filters or clean interim scratch files:
400
+
401
+ ```bash
402
+ # Update catalog criteria (e.g. restrict to QL1):
403
+ als-finder workspace update --workspace scratch/test_workspace --density QL1
404
+
405
+ # Clean scratch workspace:
406
+ als-finder workspace clean --workspace scratch/test_workspace
407
+ ```
408
+
409
+ ---
410
+
411
+ ## 🐍 Programmatic Python API
412
+
413
+ You can also import and use `als_finder` directly in Python scripts and Jupyter Notebooks:
414
+
415
+ ```python
416
+ import als_finder
417
+
418
+ # 1. Query tile specification and unbuffering bounds
419
+ spec = als_finder.get_tile_spec(
420
+ workspace="scratch/test_workspace",
421
+ tile_id=15,
422
+ tile_size=500,
423
+ buffer_size=50
424
+ )
425
+
426
+ print(f"Basename: {spec['basename']}")
427
+ print(f"Grid CRS: {spec['grid_crs']}")
428
+ print(f"Crop GDAL -te: {spec['crop_gdal_te']}")
429
+
430
+ # 2. Inspect Central Core Area vs Buffered Footprint
431
+ core = spec['core_poly']
432
+ buffered = spec['buffered_poly']
433
+
434
+ print(f"Core Tile Dimensions: {round(core.bounds[2] - core.bounds[0])}m x {round(core.bounds[3] - core.bounds[1])}m")
435
+ print(f"Buffer Extension: {round((buffered.bounds[2] - core.bounds[2]))}m on all sides")
436
+ ```
437
+
438
+ ---
439
+
440
+ ## 🏛️ Authorship & Citation
441
+
442
+ This software is released under the open-source **MIT License**. Copyright **Jonathan Greenberg**.
443
+
444
+ **Project Authors & Contributors:**
445
+ * **Jonathan Greenberg** (University of Nevada, Reno): Lead Developer and Core Project Architect.
446
+ * **Andrew Hudak** (US Forest Service): Advisory feedback and scientific computing grant alignment.
447
+ * **Antigravity (Google DeepMind)**: AI Software Engineering Partner.