Chapter 6: Spatial Allocation ============================= Learning Objectives ------------------- After reading this chapter, you should be able to: - Explain the difference between aspatial and spatial wood supply models - Use the :py:class:`ws3.spatial.ForestRaster` class for spatial allocation - Understand how raster data represents forest inventory - Allocate harvest targets to specific pixels in a landscape Aspatial vs. Spatial Models --------------------------- Wood supply models can be either **aspatial** or **spatial**: **Aspatial models** (default in ws3): - Track area by development type, not by location - Assume all stands of the same type are identical - Faster to solve, simpler to understand - Good for regional-scale planning **Spatial models**: - Track area by location (pixel-by-pixel) - Account for spatial constraints (contiguity, adjacency) - Slower to solve, more complex - Good for landscape-scale planning The :py:class:`ws3.spatial.ForestRaster` class adds spatial capability to ws3. Forest Raster Data ------------------ A **raster** is a grid of pixels (cells), where each pixel has a value. In forest inventory, each pixel might represent: - The dominant species at that location - The age class of the stand - The volume per hectare - The development type code .. mermaid:: graph TD GIS["GIS Data
(shapefiles, LiDAR)"] --> RASTERIZE["Rasterize
to grid"] RASTERIZE --> RASTER["ForestRaster
pixel grid"] RASTER --> ALLOC["Spatial allocation
assign harvest"] ALLOC --> OUTPUT["Harvest map
spatial output"] Creating a Forest Raster ------------------------ .. code-block:: python from ws3.spatial import ForestRaster # ForestRaster requires a pre-rasterized inventory GeoTIFF and a # ForestModel instance. The constructor signature is: # # ForestRaster(hdt_map, hdt_func, src_path, snk_path, # acode_map, forestmodel, base_year, ...) # # Where: # hdt_map - dict mapping hash values to development type tuples # hdt_func - function to hash development type tuples # src_path - path to input GeoTIFF (rasterized inventory) # snk_path - directory for output GeoTIFF files # acode_map - dict mapping disturbance codes to output prefixes # forestmodel - a ForestModel instance # base_year - base year for output file naming # # Example: # # raster = ForestRaster( # hdt_map=hdt_map, # hdt_func=hdt_func, # src_path="data/inventory.tif", # snk_path="output/spatial", # acode_map={"HARV": "harv", "THIN": "thin"}, # forestmodel=model, # base_year=2024 # ) Spatial Allocation via allocate_schedule ---------------------------------------- Once you have an aspatial harvest target (e.g., from optimization), you can allocate it to specific pixels using ``allocate_schedule``: .. code-block:: python # The allocate_schedule method takes a schedule (list of # (dtype_key, age, area, acode, period, etype) tuples) and # allocates it to the raster. # # Example schedule from optimization: # schedule = model.compile_schedule(problem) # # With context manager: # with ForestRaster(...) as raster: # raster.allocate_schedule(schedule) # raster.commit() # # Output GeoTIFF files are created automatically in snk_path, # one per combination of disturbance type and time step. Spatial Constraints ------------------- Common spatial constraints include: - **Contiguity**: Harvested pixels must form a single block - **Adjacency**: Harvested pixels must be adjacent to existing roads - **Setback**: Harvested pixels must be a minimum distance from water bodies - **Block size**: Harvest blocks must be between minimum and maximum sizes These constraints can be enforced by modifying the allocation algorithm or by post-processing the harvest map. Performance Considerations -------------------------- Spatial allocation is computationally intensive. Tips for improving performance: 1. **Reduce resolution**: Use coarser pixel sizes for large landscapes 2. **Subset the area**: Only allocate to relevant development types 3. **Use parallel processing**: ws3's :py:class:`ws3.forest_helper.PersistentWorkerPool` can parallelize allocation across multiple processors 4. **Cache intermediate results**: Avoid recalculating the same values Exercises --------- **Exercise 1 (Easy)**: Load a forest inventory raster and print the area by development type. **Exercise 2 (Medium)**: Allocate a harvest target of 50 hectares to a raster using the greedy method. Save the harvest map to a GeoTIFF. **Exercise 3 (Hard)**: Modify the allocation algorithm to enforce a minimum block size of 10 hectares (no harvest blocks smaller than 10 ha). Further Reading --------------- - :doc:`ch05_optimization` — Optimization fundamentals - :doc:`/howto/spatial-allocation` — Spatial schedule allocation guide - :doc:`/reference/contracts/index` — Data contracts and module boundaries