Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions docs/tutorials/point_cloud_example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

# Tutorial: Incorporate ALS Point Cloud Data into a FastFuels Domain
This tutorial demonstrates how to incorporate airborne laser scanning (ALS / LiDAR) point cloud data into an existing FastFuels domain. It utilizes a point cloud–derived canopy structure to inform the tree inventory and canopy fuel grids, enabling higher‐fidelity fuels representation.

## Prerequisites

Before starting this tutorial, make sure you have:
- FastFuels SDK installed (`pip install fastfuels-sdk`)
- A valid FastFuels API key
- Basic familiarity with Python and GeoPandas

## What You'll Learn

By the end of this tutorial, you'll know how to:
- Create an ALS point cloud within a FastFuels domain
- Generate a tree inventory informed by the ALS data

## Step 1: Create a FastFuels Domain

For the polygon created by the coordinates:
```python
import geopandas as gpd
from shapely.geometry import Polygon

# Define the polygon coordinates for Lubrecht area
coordinates = [
[-113.43635167116199,46.89738742250387],
[-113.44842074255764,46.8894785976307],
[-113.44763362920567,46.88740657162339],
[-113.44993666456837,46.8858524995899],
[-113.44923700825561,46.88429838253265],
[-113.44273603501621,46.88389988372731],
[-113.43538964373204,46.882365635689325],
[-113.42005550954369,46.88523484307359],
[-113.42329141999039,46.88919967571519],
[-113.42361209580038,46.892566564773205],
[-113.4263524163587,46.89527586047467],
[-113.42696461563226,46.895973083547176],
[-113.42853884233625,46.89916027357725],
[-113.42711037736427,46.90210825569156],
[-113.42798494775504,46.90336309078498],
[-113.42932595568779,46.903004569469715],
[-113.43241610440292,46.90099282206219],
[-113.43445676864847,46.8980248588519],
[-113.43635167116199,46.89738742250387]
]

# Create a GeoDataFrame
polygon = Polygon(coordinates)
roi = gpd.GeoDataFrame(
geometry=[polygon],
crs="EPSG:4326" # WGS 84 coordinate system
)

from fastfuels_sdk.domains import Domain

domain = Domain.from_geodataframe(
geodataframe=roi,
name="Blue Mountain ROI",
description="Test area in Lubrecht Experimental Forest",
horizontal_resolution=2.0, # 2-meter horizontal resolution
vertical_resolution=1.0 # 1-meter vertical resolution
)

print(f"Created domain with ID: {domain.id}")
```

## Step 2: Create an ALS point cloud within FastFuels

Next, use 3DEP data to generate a feature point cloud:

```python
# 1.
from fastfuels_sdk.pointclouds import PointClouds

# 2. Correct Method Name & 3. Added Required 'sources' argument
als_pointcloud = (
PointClouds.from_domain_id(domain.id)
.create_als_point_cloud(sources=["3DEP"])
)

# This works because create_als_point_cloud returns the child object
als_pointcloud.wait_until_completed()

```

## Step 3: Create Tree Inventory using the point cloud data

Create a tree inventory and generate the canopy fuel grid:

```python
from fastfuels_sdk.inventories import Inventories
from fastfuels_sdk.grids import TreeGridBuilder

# Create tree inventory
# Note: Ensure Step 2 (Point Cloud creation) is fully completed before running this
tree_inventory = Inventories.from_domain_id(
domain.id
).create_tree_inventory_from_point_cloud()
tree_inventory.wait_until_completed()
```

## Next Steps
For additional guidance and complementary workflows, refer back to the “Create and Export QUIC-Fire Inputs with FastFuels SDK” tutorial.
3 changes: 3 additions & 0 deletions fastfuels_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
)
from fastfuels_sdk.exports import Export
from fastfuels_sdk.convenience import export_roi, export_roi_to_quicfire
from fastfuels_sdk.pointclouds import PointClouds, AlsPointCloud


__all__ = [
Expand All @@ -34,4 +35,6 @@
"Export",
"export_roi",
"export_roi_to_quicfire",
"PointClouds",
"AlsPointCloud",
]
29 changes: 29 additions & 0 deletions fastfuels_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
SurfaceGridApi,
TopographyGridApi,
FeatureGridApi,
PointCloudApi,
AlsPointCloudApi,
)

_client: Optional[ApiClient] = None
Expand All @@ -32,6 +34,8 @@
_surface_grid_api: Optional[SurfaceGridApi] = None
_topography_grid_api: Optional[TopographyGridApi] = None
_feature_grid_api: Optional[FeatureGridApi] = None
_point_cloud_api: Optional[PointCloudApi] = None
_als_point_cloud_api: Optional[AlsPointCloudApi] = None


def set_api_key(api_key: str) -> None:
Expand All @@ -46,6 +50,7 @@ def set_api_key(api_key: str) -> None:
global _client, _domains_api, _inventories_api, _tree_inventory_api
global _features_api, _road_feature_api, _water_feature_api
global _grids_api, _tree_grid_api, _surface_grid_api, _topography_grid_api, _feature_grid_api
global _point_cloud_api, _als_point_cloud_api

_client = None
_domains_api = None
Expand All @@ -59,6 +64,8 @@ def set_api_key(api_key: str) -> None:
_surface_grid_api = None
_topography_grid_api = None
_feature_grid_api = None
_point_cloud_api = None
_als_point_cloud_api = None

os.environ["FASTFUELS_API_KEY"] = api_key

Expand Down Expand Up @@ -244,3 +251,25 @@ def get_feature_grid_api() -> FeatureGridApi:
if _feature_grid_api is None:
_feature_grid_api = FeatureGridApi(ensure_client())
return _feature_grid_api


def get_point_cloud_api() -> PointCloudApi:
"""Get the cached PointCloudApi instance, creating it if necessary.
Returns:
The PointCloudApi instance
"""
global _point_cloud_api
if _point_cloud_api is None:
_point_cloud_api = PointCloudApi(ensure_client())
return _point_cloud_api


def get_als_point_cloud_api() -> AlsPointCloudApi:
"""Get the cached AlsPointCloudApi instance, creating it if necessary.
Returns:
The AlsPointCloudApi instance
"""
global _als_point_cloud_api
if _als_point_cloud_api is None:
_als_point_cloud_api = AlsPointCloudApi(ensure_client())
return _als_point_cloud_api
35 changes: 35 additions & 0 deletions fastfuels_sdk/inventories.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,41 @@ def create_tree_inventory_from_file_upload(

return tree_inventory

def create_tree_inventory_from_point_cloud(
self,
in_place: bool = False,
) -> TreeInventory:
"""Create a tree inventory derived from the domain's point cloud data.
This method initiates the creation of a tree inventory by processing the
Airborne Laser Scanning (ALS) point cloud associated with this domain.
Individual trees are segmented from the point cloud to derive tree locations
and heights.
Prerequisites:
- The domain must have an existing ALS point cloud resource (see PointClouds).
- The point cloud status must be 'completed'.
Parameters
----------
in_place : bool, optional
If True, updates this Inventories object's tree inventory (self.tree).
If False (default), returns a new inventory object without modifying this instance.
Returns
-------
TreeInventory
The newly created tree inventory object. Initially in 'pending' status.
Examples
--------
>>> from fastfuels_sdk import Inventories
>>> inventories = Inventories.from_domain_id("abc123")
>>>
>>> # Create tree inventory from existing point cloud
>>> inventory = inventories.create_tree_inventory_from_point_cloud()
>>> inventory.wait_until_completed()
"""
return self.create_tree_inventory(
sources=[TreeInventorySource.POINTCLOUD],
in_place=in_place,
)


class TreeInventory(TreeInventoryModel):
"""
Expand Down
Loading
Loading