Skip to content

Generate Model

TerrainType

Bases: Enum

Categorical enumeration defining landscape complexities for ground filtering tolerances.

Source code in src/phytospatial/lidar/generate_model.py
class TerrainType(Enum):
    """
    Categorical enumeration defining landscape complexities for ground filtering tolerances.
    """
    FLAT = 1
    RELIEF = 2
    HIGH_RELIEF = 3

generate_chm(source, resolution, crs, terrain=TerrainType.RELIEF, chunk_size=2000000, force_csf=False, filter_size=3)

Constructs a finalized Canopy Height Model entirely in-memory using a fused single-pass map execution, eliminating redundant disk streaming and intermediate layer serializations.

Parameters:

Name Type Description Default
source Union[str, Path, PointCloud]

Source coordinate spatial tensor.

required
resolution float

Matrix subdivision granularity.

required
crs Union[str, CRS]

Defined projection system.

required
terrain TerrainType

Modifies physics bounds for cloth drop.

RELIEF
chunk_size int

Stream slicing bounds for RAM safety limits.

2000000
force_csf bool

Bypass existing structural classification checks.

False
filter_size int

Matrix kernel mapping for salt-and-pepper noise dampening.

3

Returns:

Name Type Description
Raster Raster

Cleaned, height-normalized canopy architecture model.

Source code in src/phytospatial/lidar/generate_model.py
def generate_chm(
    source: Union[str, Path, PointCloud],
    resolution: float,
    crs: Union[str, CRS],
    terrain: TerrainType = TerrainType.RELIEF,
    chunk_size: int = 2_000_000,
    force_csf: bool = False,
    filter_size: int = 3
    ) -> Raster:
    """
    Constructs a finalized Canopy Height Model entirely in-memory using a fused single-pass map execution, 
    eliminating redundant disk streaming and intermediate layer serializations.

    Args:
        source (Union[str, Path, PointCloud]): Source coordinate spatial tensor.
        resolution (float): Matrix subdivision granularity.
        crs (Union[str, CRS]): Defined projection system.
        terrain (TerrainType): Modifies physics bounds for cloth drop.
        chunk_size (int): Stream slicing bounds for RAM safety limits.
        force_csf (bool): Bypass existing structural classification checks.
        filter_size (int): Matrix kernel mapping for salt-and-pepper noise dampening.

    Returns:
        Raster: Cleaned, height-normalized canopy architecture model.
    """
    dsm_grid, dtm_grid, transform, d_search, t_search = _generate_base_surfaces(
        source, resolution, terrain, chunk_size, force_csf, compute_dtm=True
    )

    dsm_grid[dsm_grid == -np.inf] = np.nan
    dtm_grid[dtm_grid == np.inf] = np.nan

    dsm_valid = ~np.isnan(dsm_grid)
    dtm_valid = ~np.isnan(dtm_grid)

    data_footprint = dsm_valid.copy()
    data_footprint = ndimage.binary_closing(data_footprint, structure=np.ones((5, 5)))
    data_footprint = ndimage.binary_fill_holes(data_footprint)

    if np.any(dtm_valid) and not np.all(dtm_valid):
        dtm_grid = fillnodata(dtm_grid, mask=dtm_valid.astype(np.uint8), max_search_distance=t_search)

    if np.any(dsm_valid) and not np.all(dsm_valid):
        dsm_grid = fillnodata(dsm_grid, mask=dsm_valid.astype(np.uint8), max_search_distance=d_search)

    chm_arr = dsm_grid - dtm_grid
    chm_arr[chm_arr < 0.0] = 0.0

    if filter_size > 0:
        chm_arr = np.where(data_footprint, ndimage.median_filter(chm_arr, size=filter_size), np.nan)

    chm_arr[~data_footprint] = NODATA_VAL
    chm_arr[np.isnan(chm_arr)] = NODATA_VAL

    return Raster(
        data=chm_arr.astype(np.float32),
        transform=transform,
        crs=crs,
        nodata=NODATA_VAL,
        band_names={"CHM": 1}
    )

generate_dsm(source, resolution, crs, chunk_size=2000000)

Extracts and filters the maximum vegetation footprint from the unified spatial pipeline.

Parameters:

Name Type Description Default
source Union[str, Path, PointCloud]

Target geometric entity or byte file.

required
resolution float

Square dimension of discrete matrix cells in coordinate metrics.

required
crs Union[str, CRS]

Projection and datum specifications.

required
chunk_size int

Execution point limit strictly scaling active RAM utilization.

2000000

Returns:

Name Type Description
Raster Raster

Sealed and hole-filled contiguous surface footprint.

Source code in src/phytospatial/lidar/generate_model.py
def generate_dsm(
    source: Union[str, Path, PointCloud],
    resolution: float,
    crs: Union[str, CRS],
    chunk_size: int = 2_000_000
    ) -> Raster:
    """
    Extracts and filters the maximum vegetation footprint from the unified spatial pipeline.

    Args:
        source (Union[str, Path, PointCloud]): Target geometric entity or byte file.
        resolution (float): Square dimension of discrete matrix cells in coordinate metrics.
        crs (Union[str, CRS]): Projection and datum specifications.
        chunk_size (int): Execution point limit strictly scaling active RAM utilization.

    Returns:
        Raster: Sealed and hole-filled contiguous surface footprint.
    """
    dsm_grid, _, transform, d_search, _ = _generate_base_surfaces(
        source, resolution, TerrainType.FLAT, chunk_size, force_csf=False, compute_dtm=False
    )

    dsm_grid[dsm_grid == -np.inf] = np.nan
    valid_mask = ~np.isnan(dsm_grid)

    data_footprint = valid_mask.copy()
    data_footprint = ndimage.binary_closing(data_footprint, structure=np.ones((5, 5)))
    data_footprint = ndimage.binary_fill_holes(data_footprint)

    if np.any(valid_mask) and not np.all(valid_mask):
        dsm_grid = fillnodata(dsm_grid, mask=valid_mask.astype(np.uint8), max_search_distance=d_search)
    elif not np.any(valid_mask):
        dsm_grid[:] = NODATA_VAL

    dsm_grid[~data_footprint] = NODATA_VAL
    dsm_grid[np.isnan(dsm_grid)] = NODATA_VAL

    return Raster(
        data=dsm_grid.astype(np.float32),
        transform=transform,
        crs=crs,
        nodata=NODATA_VAL
    )

generate_dtm(source, resolution, crs, terrain=TerrainType.RELIEF, chunk_size=2000000, force_csf=False)

Extracts and interpolates the minimum bare earth elevation footprint from the unified spatial pipeline.

Parameters:

Name Type Description Default
source Union[str, Path, PointCloud]

Target geometric entity or byte file.

required
resolution float

Square dimension of discrete matrix cells in coordinate metrics.

required
crs Union[str, CRS]

Projection and datum specifications.

required
terrain TerrainType

Modifies internal simulation tension limits for complex landscapes.

RELIEF
chunk_size int

Execution point limit strictly scaling active RAM utilization.

2000000
force_csf bool

Circumvents explicit classifications to re-simulate ground masks.

False

Returns:

Name Type Description
Raster Raster

Interpolated and sealed continuous surface.

Source code in src/phytospatial/lidar/generate_model.py
def generate_dtm(
    source: Union[str, Path, PointCloud],
    resolution: float,
    crs: Union[str, CRS],
    terrain: TerrainType = TerrainType.RELIEF,
    chunk_size: int = 2_000_000,
    force_csf: bool = False
    ) -> Raster:
    """
    Extracts and interpolates the minimum bare earth elevation footprint from the unified spatial pipeline.

    Args:
        source (Union[str, Path, PointCloud]): Target geometric entity or byte file.
        resolution (float): Square dimension of discrete matrix cells in coordinate metrics.
        crs (Union[str, CRS]): Projection and datum specifications.
        terrain (TerrainType): Modifies internal simulation tension limits for complex landscapes.
        chunk_size (int): Execution point limit strictly scaling active RAM utilization.
        force_csf (bool): Circumvents explicit classifications to re-simulate ground masks.

    Returns:
        Raster: Interpolated and sealed continuous surface.
    """
    _, dtm_grid, transform, _, t_search = _generate_base_surfaces(
        source, resolution, terrain, chunk_size, force_csf, compute_dtm=True
    )

    dtm_grid[dtm_grid == np.inf] = np.nan
    valid_mask = ~np.isnan(dtm_grid)

    if np.any(valid_mask) and not np.all(valid_mask):
        dtm_grid = fillnodata(dtm_grid, mask=valid_mask.astype(np.uint8), max_search_distance=t_search)
    elif not np.any(valid_mask):
        dtm_grid[:] = NODATA_VAL

    dtm_grid[np.isnan(dtm_grid)] = NODATA_VAL

    return Raster(
        data=dtm_grid.astype(np.float32),
        transform=transform,
        crs=crs,
        nodata=NODATA_VAL
    )