Overview

This manual is the long-form user guide for WbW-QGIS.

WbW-QGIS (Whitebox Workflows for QGIS) is the QGIS frontend for Whitebox Next Gen. It provides a QGIS-native way to discover, configure, and run Whitebox tools through the Processing framework and plugin UI.

Whitebox Next Gen uses a layered architecture:

  • backend geospatial engines and tools in Rust,
  • frontend runtimes for Python, R, and QGIS,
  • shared tool taxonomy and capability metadata.

Whitebox Next Gen is intentionally full-stack: core geospatial capabilities that are often delegated to external C/C++ dependencies in other GIS platforms (for example raster I/O, projections, geometry/topology operations, and lidar handling) are implemented in the Whitebox codebase itself. This architecture is unusual in GIS and provides practical benefits for users: consistent behavior across platforms, tighter control over correctness and performance, fewer system-level dependency issues during installation, and faster iteration when fixing bugs or introducing new capabilities.

Within this model, WbW-QGIS is intentionally a thin integration layer. It handles QGIS presentation and orchestration while computation remains in the Whitebox backend runtime.

What This Manual Covers

This guide focuses on practical use of WbW-QGIS:

  • setting up the plugin and runtime correctly,
  • understanding discovery and provider refresh,
  • running tools through QGIS Processing,
  • handling output and troubleshooting common issues.

The manual is written for both analysts and developers who use QGIS as the primary working environment.

Goals

  • Provide a stable onboarding path for local installation.
  • Document the operational behavior of plugin discovery and execution.
  • Clarify tier and licensing behavior in QGIS.
  • Reduce setup friction and runtime ambiguity.

How to Use This Manual

For first-time setup, read chapters in order:

  1. Installation and Setup
  2. Build and Preview
  3. Quick Start
  4. Runtime and Discovery

After setup is stable, use the remaining chapters as reference material.

Installation and Setup

This chapter covers local installation of WbW-QGIS from source.

Prerequisites

  • QGIS 4.x
  • A Python environment used by QGIS
  • Local checkout of whitebox_next_gen

Install the Whitebox Python Runtime

From the repository root, install whitebox_workflows into the same Python environment used by QGIS:

./scripts/dev_python_install.sh

If you are working in an environment that supports Pro-enabled integration builds, use:

./scripts/dev_python_install.sh --pro

Plugin source directory:

  • crates/wbw_qgis/plugin/whitebox_workflows_qgis

Target QGIS plugins directory:

  • /python/plugins/whitebox_workflows_qgis

Typical local workflow from repository root:

export QGIS_PLUGIN_DIR="<QGIS settings dir>/python/plugins"
mkdir -p "$QGIS_PLUGIN_DIR"
ln -snf "$PWD/crates/wbw_qgis/plugin/whitebox_workflows_qgis" \
  "$QGIS_PLUGIN_DIR/whitebox_workflows_qgis"

Verify Python Import Path

Before opening QGIS, verify whitebox_workflows import in the same Python environment QGIS uses:

python3 -c "import whitebox_workflows as wb; print(wb.__file__)"

If this fails, fix environment alignment before continuing.

Build and Preview

The WbW-QGIS manual uses mdBook, matching the WbW-Python and WbW-R manuals.

Build the Manual

From the manual directory:

cd crates/wbw_qgis/manual
mdbook build

Generated output will be written to:

  • crates/wbw_qgis/manual/book

Live Preview

For a local preview server:

cd crates/wbw_qgis/manual
mdbook serve --open

This starts a local server and opens the manual in your browser.

Writing Conventions

  • Keep examples task-oriented and reproducible.
  • Prefer short, complete QGIS workflows over abstract API descriptions.
  • Document expected outputs and validation checks where possible.

Quick Start

This walkthrough verifies that WbW-QGIS is running and can execute tools.

1. Enable Plugin

  1. Start QGIS.
  2. Open Plugin Manager.
  3. Enable Whitebox Workflows.

2. Confirm Provider Availability

  1. Open the Processing Toolbox.
  2. Confirm Whitebox provider entries appear.
  3. If tools are missing, trigger a discovery refresh from the plugin panel.

3. Run a First Tool

A common smoke test is a simple raster analysis tool with a small input file.

Recommended pattern:

  1. Choose a small test raster.
  2. Run a lightweight tool from the Whitebox provider.
  3. Write output to a temporary file.
  4. Load and inspect the output layer in QGIS.

4. Validate Results

  • Confirm the output file exists.
  • Confirm layer metadata and CRS are as expected.
  • Confirm visual result is plausible for the input.

If any step fails, continue to the Troubleshooting chapter.

Runtime and Discovery

WbW-QGIS discovers tool availability at runtime using the active whitebox_workflows environment.

Discovery Flow

At a high level:

  1. Import whitebox_workflows in the QGIS Python environment.
  2. Create a runtime session.
  3. Read runtime capability metadata.
  4. Read tool catalog metadata.
  5. Partition available vs locked tools.
  6. Refresh Processing provider algorithms.

When to Refresh

Refresh discovery when:

  • plugin settings change,
  • runtime tier/entitlement changes,
  • whitebox_workflows is rebuilt or reinstalled,
  • tool taxonomy updates are introduced.

Common Discovery Symptoms

  • Provider missing entirely: plugin import/runtime bootstrap failure.
  • Provider appears but tools are absent: catalog read failure or stale cache.
  • Tools show as locked unexpectedly: runtime capability/tier mismatch.

In all cases, validate environment alignment first.

Tool Execution in QGIS

WbW-QGIS executes tools through the QGIS Processing framework.

Typical Execution Path

  1. Select a Whitebox algorithm in Processing Toolbox.
  2. Fill parameters in the algorithm dialog.
  3. Execute and monitor progress/messages.
  4. Load or inspect output artifacts.
  • Use explicit output paths for reproducibility.
  • Start with small representative datasets before full runs.
  • Validate intermediate outputs for CRS, schema, and metadata.
  • Keep task logs for long workflows and batch operations.

Output Handling

Whitebox tools may produce:

  • raster outputs,
  • vector outputs,
  • lidar outputs,
  • text/report sidecar artifacts.

Confirm output type and format before chaining into downstream steps.

Progress and Messaging

Execution status and warnings should be treated as part of result validation. If a tool completes with warnings, inspect outputs before continuing.

Recipes

Recipes in WbW-QGIS are guided workflow entries that help users launch common multi-tool patterns faster.

A recipe is not a new backend algorithm. It is a curated sequence of existing tools with summary guidance, launch defaults, and tier-aware visibility.

What Recipes Provide

Recipes provide:

  • A short purpose statement.
  • A launch tool (the first tool dialog opened when you run the recipe).
  • A step list (ordered tool IDs for the workflow).
  • Optional input and output hints.
  • Tier gating (Open, Pro, or Enterprise).

Where Recipes Appear in QGIS

Recipes are available in the Whitebox Workflows dock panel under Workflow Recipes.

The panel includes:

  • Open Recipe
  • Copy Recipe Steps
  • Why Is This Locked?
  • Open Recipe File
  • Reload Recipe File
  • Validate Recipe File
  • Include locked recipes toggle

Built-in and User Recipes

WbW-QGIS merges two sources:

  1. Built-in recipes shipped with the plugin.
  2. User-defined recipes loaded from a local JSON file.

If a user recipe has the same id as a built-in recipe, the user recipe overrides the built-in entry.

User Recipe File Location

Default file path:

  • ~/.whitebox_workflows_qgis/recipes.json

Override path with environment variable:

  • WBW_QGIS_USER_RECIPES

When you press Open Recipe File, the plugin creates the file from a template if it does not exist.

User Recipe File Format

The file may be either:

  1. An object with a recipes array.
  2. A direct array of recipes.

Each recipe should include:

  • id (required)
  • tools array (required)

Optional fields:

  • title
  • summary
  • tier (open, pro, enterprise)
  • launch_tool
  • input_hint
  • output_hint

Example:

{
  "recipes": [
    {
      "id": "my_custom_terrain_recipe",
      "title": "My Custom Terrain Recipe",
      "summary": "User-defined recipe example.",
      "tier": "open",
      "launch_tool": "slope",
      "tools": ["slope", "aspect", "hillshade"],
      "input_hint": "Set a DEM raster as the primary input.",
      "output_hint": "Write outputs to a dedicated project output folder."
    }
  ]
}

Validation and Error Reporting

Use Validate Recipe File in the panel to run validation and see a full report.

Validation checks include:

  • Entry structure is a JSON object.
  • id exists and is non-empty.
  • tools exists and is a non-empty array.
  • tier value is valid when supplied.

Invalid entries are skipped, while valid entries continue to load.

Warnings include recipe index and, when available, recipe id to speed up fixes.

Recipe Visibility and Tier Behavior

Recipes are filtered by:

  • Runtime tier entitlement.
  • Tool availability in the current runtime catalog.
  • Include locked recipes panel setting.

When Include locked recipes is enabled, recipes that are not runnable in the current runtime remain visible with lock messaging for discovery.

Discovery and Sorting

Recipes are shown alphabetically in the panel for easier scanning.

Sorting applies to both built-in and user-defined recipes.

For teams, keep a shared recipe JSON under version control and point WBW_QGIS_USER_RECIPES to that path in your local environment setup.

This gives you repeatable, reviewable workflow definitions without modifying plugin source files.

Licensing and Tiers

Whitebox NG is available in two licensing tiers with different capabilities and licensing models.

License Tiers

  • Open Tier (free): Governed by MIT/Apache 2.0 dual licensing. All Open-tier tools are free and open-source with no entitlement or activation required. Use this tier for learning, research, and open development.

  • Pro Tier (commercial): Proprietary software governed by EULA. Pro-tier tools provide advanced capabilities and require activation with a valid license key. Once activated, the license persists locally so you do not need to re-authenticate on each QGIS session.

How QGIS Reflects Licensing

The Whitebox Workflows QGIS plugin is a frontend layer. Licensing authority and rules are enforced in the backend runtime.

Core Principle

The plugin reflects backend capabilities; it does not define licensing rules. Runtime mode (open vs. pro) determines which tools are available and functional.

Practical Behavior

  • Open-tier tools are expected to run in all standard public QGIS environments.
  • Pro-tier tools may be visible in the plugin but locked without an active Pro license.
  • You can request a specific tier, but the effective tier depends on your entitlement state.

Why This Matters

  • One plugin surface adapts to both open and pro capability tiers.
  • Tool discovery remains consistent across Python, R, and QGIS frontends.
  • Licensing decisions and enforcement remain centralized in backend logic.

Interactive License Management

The plugin provides convenient menu actions for license management without requiring external tools or command lines.

Activating a Pro License

  1. In QGIS, navigate to Plugins > Whitebox Workflows > Activate License.
  2. Enter your license information when prompted:
    • License key (required)
    • First name (required)
    • Last name (required)
    • Email (required)
    • Provider URL (optional; defaults to production)
  3. Accept the EULA terms.
  4. Click OK. The plugin will activate and persist your license locally.
  5. The tool catalog automatically refreshes to show Pro-tier tools.

Important: License activation is tied to your machine. See Transferring a License to move to another machine.

Checking License Status

Navigate to Plugins > Whitebox Workflows > Plugin Settings (or look for diagnostics output) to see:

  • License validity (active or expired)
  • Effective tier (open or pro)
  • License expiration time

Transferring a License

If you need to use your Pro license on a different machine, you must first deactivate it on the current machine and then activate on the destination.

  1. On the current machine, navigate to Plugins > Whitebox Workflows > Transfer License. This generates a portable activation payload and clears your local license state.
  2. Share the activation payload with the destination machine (or keep it for your own use on the other machine).
  3. On the destination machine, navigate to Plugins > Whitebox Workflows > Activate License and enter your license key using the same process as above. The destination will obtain its own local license state.

Deactivating a License

If you no longer plan to use Pro tools on this machine, navigate to Plugins > Whitebox Workflows > Deactivate License. This clears your local license state. Future sessions will fall back to Open-tier tools only.

Local License State

Once activated, your license information is stored locally at ~/.whitebox/wbw_ng_license_state.json (or override via the WBW_LICENSE_STATE_PATH environment variable). On each QGIS startup:

  • If valid local state exists, it is automatically loaded.
  • If local state is expired or missing, the plugin falls back to Open-tier mode.
  • You do not need to re-authenticate in QGIS on every session.

Expected Local-Dev Outcome

For most source-based setups, assume open-tier behavior unless your runtime environment is explicitly configured for Pro-enabled integration testing or you have an active Pro license activated on your machine.

Supported Data Formats

This chapter documents format support exposed through WbW-QGIS.

Authoritative backend support comes from Whitebox core crates:

  • Raster I/O: wbraster
  • Vector I/O: wbvector
  • LiDAR I/O: wblidar

The format tables below are aligned with those backend crates' README "Supported Formats" sections.


Raster Formats

Raster support in Whitebox is provided by wbraster.

FormatExtension(s)ReadWriteNotes
DTED.dt0, .dt1, .dt2YesYesDTED 0/1/2 elevation; WGS-84 geographic only
ENVI HDR Labelled.hdr + sidecar dataYesYesMulti-band (BSQ / BIL / BIP)
ER Mapper.ers + dataYesYesHierarchical header
ERDAS IMAGINE (HFA).imgYesNoRead-only MVP; RLC compression supported
Esri ASCII Grid.asc, .grdYesYesHandles xllcorner and xllcenter
Esri Binary Gridworkspace dir / .adfYesYesSingle-band float32, big-endian
Esri Float Grid.flt, .hdrYesYesSingle-band float grid with header
JPEG + World File.jpg, .jpeg + .jgw/.wldYesYesNon-rotated georeferencing
PNG + World File.png + .pgw/.wldYesYesNon-rotated georeferencing
GeoTIFF / BigTIFF / COG.tif, .tiffYesYesStripped/tiled GeoTIFF, BigTIFF, COG
GeoPackage Raster (Phase 4).gpkgYesYesMulti-band tiled raster
GRASS ASCII Raster.asc, .txtYesYesnorth/south/east/west, rows/cols headers
Idrisi/TerrSet Raster.rdc, .rstYesYesbyte, integer, real, RGB24
JPEG2000 / GeoJP2.jp2YesYesPure-Rust reader and writer
PCRaster.mapYesYesValue-scale aware writer
SAGA GIS Binary.sgrd, .sdatYesYesSAGA data types supported
Surfer GRD.grdYesYesDSAA and DSRB
Zarr v2/v3.zarrYesYes2D and 3D (band,y,x) chunked arrays
XYZ ASCII Grid.xyzYesYesWhitespace or comma-delimited X Y Z points

Notes:

  • Whitebox avoids runtime dependence on GDAL.
  • In QGIS workflows, GeoTIFF remains the safest default interchange raster.

Vector Formats

Vector support in Whitebox is provided by wbvector.

FormatReadWriteNotes
FlatGeobuf (.fgb)YesYesHigh-performance binary interchange
GeoJSON (.geojson)YesYesWeb-friendly text format
TopoJSON (.topojson)YesYesTopology-preserving JSON format
GeoPackage (.gpkg)YesYesSQLite container; multi-layer workflows
GML (.gml)YesYesStandards-based XML exchange
GPX (.gpx)YesYesGPS tracks/routes/waypoints
KML (.kml)YesYesGoogle Earth-style visualization
MapInfo Interchange (.mif + .mid)YesYesLegacy MapInfo interoperability
ESRI Shapefile (.shp + sidecars)YesYesBroad legacy compatibility
GeoParquet (.parquet)YesYesOptional geoparquet feature
KMZ (.kmz)YesYesOptional kmz feature
OSM PBF (.osm.pbf)YesNoRead-only; optional osmpbf feature

Feature-gated formats in wbvector:

  • geoparquet for GeoParquet support
  • kmz for KMZ support
  • osmpbf for OSM PBF read support

In QGIS workflows, GeoPackage and FlatGeobuf are good modern interchange choices; Shapefile remains a compatibility fallback.


LiDAR / Point Cloud Formats

LiDAR support in Whitebox is provided by wblidar.

FormatReadWriteNotes
LASYesYesLAS 1.1-1.5, PDRF 0-15
LAZYesYesStandards-compliant LASzip v2/v3 Point10/Point14 codecs
COPCYesYesCOPC 1.0 hierarchy with Point14-family payloads
PLYYesYesASCII, binary little-endian, binary big-endian
E57YesYesASTM E2807 with CRC-32 page validation

Optional features in wblidar:

  • copc-http for HTTP range fetching of remote COPC
  • copc-parallel for parallel COPC writing paths
  • laz-parallel for optional parallel LAZ decode paths
  • parallel umbrella feature (enables both parallel paths)

In QGIS workflows, .copc.laz is a strong default for large point-cloud delivery and archive.


QGIS Practical Defaults

For most WbW-QGIS production workflows:

  • Raster default: GeoTIFF (.tif)
  • Vector default: GeoPackage (.gpkg) or FlatGeobuf (.fgb)
  • LiDAR default: COPC LAZ (.copc.laz) or LAZ (.laz)

These defaults balance compatibility, file size, and performance.


Important Distinction

Backend format support means the Whitebox runtime can read/write those formats. Specific QGIS tool dialogs may still constrain certain outputs or defaults depending on parameter wiring and the tool category.

When in doubt:

  1. Use the tool's default output extension in QGIS.
  2. Re-open output and validate metadata.
  3. Use QGIS conversion tools only when you need a different interchange format.

Common Format Problems

ProblemLikely causeFix
Output opens but schema is unexpectedFormat-specific field/type constraintsUse GeoPackage or FlatGeobuf for richer schema
Shapefile field names truncated10-character DBF limitSwitch output to GeoPackage
Large cloud is slow to browseNon-indexed point-cloud formatUse COPC LAZ for tiled access
Optional format not availableFeature not enabled in buildUse a non-optional format (for example GeoPackage/GeoJSON/Shapefile)
CRS appears missing in outputSidecar or metadata issueConfirm CRS in layer properties and re-export if needed

Reprojection and CRS

A Coordinate Reference System (CRS) defines how coordinates in a dataset map to real-world locations. CRS mismatches are one of the most common sources of silent errors in GIS workflows: two layers may display correctly on screen (because QGIS reprojects them on-the-fly for display) while producing wrong results when passed to an analysis tool that expects matching CRS inputs.

This chapter explains how to identify, verify, and correct CRS issues in WbW-QGIS workflows.


Key Concepts

  • Geographic CRS (GCS): Coordinates in angular units (degrees of latitude and longitude). Common examples: WGS84 (EPSG:4326), NAD83 (EPSG:4269). Not suitable as a working CRS for distance/area calculations.
  • Projected CRS (PCS): Coordinates in linear units (metres or feet) on a flat map projection. Examples: UTM zones, Lambert Conformal Conic, Albers Equal Area.
  • EPSG code: A numeric registry identifier for a CRS. EPSG:4326 = WGS84; EPSG:32617 = UTM Zone 17N (WGS84); EPSG:3978 = Canada Atlas Lambert.
  • On-the-fly reprojection: QGIS displays all layers in the project CRS regardless of their native CRS. This is for display only — it does not change the file on disk.
  • Reproject (warp): Permanently transform raster or vector data to a new CRS, writing a new file. Required before passing data to analysis tools.
  • Z factor: A unit-conversion factor applied when DEM horizontal units (metres) differ from vertical units (feet), or vice versa.

Choosing a Working CRS

ScenarioRecommended CRS type
Global or continental analysisGeographic (WGS84 / EPSG:4326) for data exchange; Equal-Area projection for area measurements
Regional / national analysisNational projected CRS (e.g. Canada Atlas Lambert / EPSG:3978)
Local analysis (< 500 km extent)UTM zone covering the study area
Terrain analysis, hydrology, LiDARProjected CRS in metres (UTM recommended)
Slope / distance calculationsAlways use a projected CRS

Finding your UTM zone: The UTM zone number equals ⌊(longitude + 180) / 6⌋ + 1. For Ottawa, Canada (longitude ≈ –75.7°): zone 18, northern hemisphere → EPSG:32618.


Checking the CRS of a Layer

  1. Right-click a layer in the Layers panel → Properties.
  2. Select the Information tab.
  3. Read the CRS field. Confirm:
    • Authority and code (e.g. EPSG:32618)
    • Unit (metres vs degrees)
    • Datum (WGS84, NAD83, etc.)

Or in the Python Console:

from qgis.core import QgsProject

layer = QgsProject.instance().mapLayersByName('dem')[0]
crs = layer.crs()
print(crs.authid())        # e.g. "EPSG:32618"
print(crs.mapUnits())      # 0 = metres, 6 = degrees
print(crs.isGeographic())  # True if GCS

Setting the Project CRS

The project CRS controls the display and the default output CRS for tools that do not inherit CRS from their inputs.

Project → Properties → CRS tab → search by EPSG code or name → click OK.

Or use View → Panels → CRS Status at the bottom-right of the QGIS window to set the project CRS from any loaded layer.


Reprojecting a Raster

Use Reproject Raster to permanently transform a raster to a new CRS. This is required before any terrain analysis on a DEM stored in geographic (degree) coordinates.

Processing Toolbox → Whitebox Workflows → Raster → Reproject Raster

ParameterRecommended value
Input layerdem_wgs84.tif
Target EPSG code32618
Resampling methodbilinear (elevation surfaces)
Outputdem_utm18n.tif

The Resampling method parameter accepts any of the methods supported by WbW's raster engine:

MethodBest for
nearestCategorical / integer rasters (classification maps, stream grids)
bilinearContinuous surfaces (DEMs, slope, TWI, reflectance)
cubicHigh-quality continuous-surface resampling
lanczosHigh-quality sinc-window resampling
average3×3 mean statistic
min / max3×3 extremum statistics
mode3×3 majority-class (smoothed categorical)
median3×3 median statistic
stddev3×3 standard deviation
import processing

processing.run('whitebox_workflows:reproject_raster', {
    'input': '/data/dem_wgs84.tif',
    'epsg': 32618,
    'resample': 'bilinear',
    'output': '/data/dem_utm18n.tif',
})

After running, load the output and confirm in Layer Properties → Information: CRS shows EPSG:32618 and the extent is in metres.


Reprojecting a Vector Layer

Use Reproject Vector to transform a vector dataset to a new CRS.

Processing Toolbox → Whitebox Workflows → Vector → Reproject Vector

ParameterRecommended value
Input layerroads_wgs84.shp
Target EPSG code32618
Outputroads_utm18n.shp
import processing

processing.run('whitebox_workflows:reproject_vector', {
    'input': '/data/roads_wgs84.shp',
    'epsg': 32618,
    'output': '/data/roads_utm18n.shp',
})

Reprojecting a LiDAR Dataset

Use Reproject LiDAR to transform a point cloud to a new CRS.

Processing Toolbox → Whitebox Workflows → LiDAR → Reproject LiDAR

ParameterRecommended value
Input LiDAR filecloud_wgs84.laz
Target EPSG code32618
Outputcloud_utm18n.laz
import processing

processing.run('whitebox_workflows:reproject_lidar', {
    'input': '/data/cloud_wgs84.laz',
    'epsg': 32618,
    'output': '/data/cloud_utm18n.laz',
})

Assigning a Missing CRS

If a file has correct coordinates but missing or wrong CRS metadata (shown as "Unknown CRS" in Layer Properties), use one of the three Assign Projection tools to write the correct EPSG code into the file without moving any coordinates.

Assign vs. Reproject: Assigning a CRS only updates the metadata label. Use it when coordinates are already in the target system but the file has no CRS tag. If the coordinate values themselves need to change, use the Reproject tools above instead.

Assign Projection to a Raster

Processing Toolbox → Whitebox Workflows → Raster → Assign Projection Raster

ParameterValue
Input layerdem_no_crs.tif
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_raster', {
    'input': '/data/dem_no_crs.tif',
    'epsg': 32618,
})

Assign Projection to a Vector

Processing Toolbox → Whitebox Workflows → Vector → Assign Projection Vector

ParameterValue
Input layerroads_no_crs.shp
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_vector', {
    'input': '/data/roads_no_crs.shp',
    'epsg': 32618,
})

Assign Projection to a LiDAR File

Processing Toolbox -> Whitebox Workflows -> LiDAR -> Assign Projection LiDAR

ParameterValue
Input LiDAR filecloud_no_crs.laz
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_lidar', {
  'input': '/data/cloud_no_crs.laz',
  'epsg': 32618,
})

Georeferencing from Control Points

Use Georeference Raster From Control Points when the raster has no valid georeferencing and you have control points that relate pixel coordinates to map coordinates.

Processing Toolbox -> Whitebox Workflows -> Raster -> Georeference Raster From Control Points

ParameterRecommended value
Input rasterhistorical_scan.tif
Control points CSVhistorical_scan_gcps.csv
Destination EPSG code32618
Resampling methodbilinear (continuous) or nearest (categorical)
Georeferenced raster outputhistorical_scan_georef.tif
Diagnostics report outputhistorical_scan_georef_report.json (optional)

Control-points CSV fields must include source image coordinates and target map coordinates:

  • source_col
  • source_row
  • target_x
  • target_y
import processing

processing.run('whitebox_workflows:georeference_raster_from_control_points', {
  'input': '/data/historical_scan.tif',
  'control_points': '/data/historical_scan_gcps.csv',
  'epsg': 32618,
  'resample': 'bilinear',
  'output': '/data/historical_scan_georef.tif',
  'report': '/data/historical_scan_georef_report.json',
})

Assign Projection to a LiDAR Dataset

Processing Toolbox → Whitebox Workflows → LiDAR → Assign Projection LiDAR

ParameterValue
Input LiDAR filecloud_no_crs.laz
EPSG code to assign32618
import processing

processing.run('whitebox_workflows:assign_projection_lidar', {
    'input': '/data/cloud_no_crs.laz',
    'epsg': 32618,
})

The Z Factor for Terrain Tools

When a DEM's horizontal units differ from its vertical units, slope and curvature calculations are incorrect unless a Z factor is applied.

Horizontal unitVertical unitZ factor
MetresMetres1.0 (no conversion needed)
MetresFeet0.3048
FeetFeet1.0
Degrees (geographic)MetresDo not use — reproject first

All WbW terrain tools that accept a Z factor parameter apply the conversion as: slope = atan(rise × z_factor / run).

Best practice: Reproject the DEM to a projected CRS in metres before running any terrain analysis. Set Z factor to 1.0 after reprojection if vertical units are also metres.


Batch Reprojection via Python Console

Reproject all GeoPackages in a folder to EPSG:32618 using the Whitebox vector reprojection tool:

import processing
from pathlib import Path

src_dir = Path('/data/raw_vectors')
out_dir = Path('/data/projected')
out_dir.mkdir(exist_ok=True)

for gpkg in src_dir.glob('*.gpkg'):
    out = out_dir / gpkg.name
    processing.run('whitebox_workflows:reproject_vector', {
        'input': str(gpkg),
        'epsg': 32618,
        'output': str(out),
    })
    print(f"Reprojected: {gpkg.name}")

print("Batch reprojection complete.")

Reproject a folder of LiDAR files in the same pattern:

import processing
from pathlib import Path

src_dir = Path('/data/raw_lidar')
out_dir = Path('/data/projected_lidar')
out_dir.mkdir(exist_ok=True)

for las in src_dir.glob('*.laz'):
    out = out_dir / las.name
    processing.run('whitebox_workflows:reproject_lidar', {
        'input': str(las),
        'epsg': 32618,
        'output': str(out),
    })
    print(f"Reprojected: {las.name}")

print("Batch LiDAR reprojection complete.")

Common CRS Problems

ProblemLikely causeFix
Layers display in wrong locationLayer has incorrect assigned CRSAssign correct CRS (do not reproject)
Slope values in hundreds of degreesDEM in geographic CRS (degrees) — cell size << 1°Reproject DEM to metres before running slope
Area calculations wildly wrongLayer CRS is geographic (degrees)Reproject to equal-area projected CRS
Watershed does not close properlyRaster and vector inputs in different CRSReproject all inputs to same CRS before processing
WbW tool silently returns NoData everywhereCRS mismatch causes spatial extents not to overlapVerify all inputs share the same CRS and extent
"Datum transform not found" warning in QGISDatum shift grid file not installedInstall proj-data package, or accept approximate transform

Validation Checklist

  • Project CRS is set to the intended working CRS before analysis.
  • All raster inputs share the same CRS, extent, and cell size.
  • All vector inputs share the same CRS as the raster grid.
  • DEM CRS is projected (linear units — metres or feet), not geographic.
  • Z factor is set to 1.0 when both horizontal and vertical units are metres.
  • Reprojected outputs have been inspected (extent in metres, CRS code confirmed).
  • No layers show "Unknown CRS" in the Layers panel.

Terrain Analysis and Geomorphometry

Terrain analysis — or geomorphometry — is the quantitative characterisation of land-surface form from digital elevation models (DEMs). It is one of the original strengths of the Whitebox platform and covers first-order derivatives (slope, aspect), curvature families, terrain position, roughness, multiscale analysis, and visibility.

This chapter walks through a complete primary-derivative workflow in the QGIS Processing Toolbox, followed by a Python console version for batch scripting.


Key Concepts

  • DEM: Raster where each cell stores surface elevation. All terrain derivatives begin here. Common sources: LiDAR bare-earth, drone photogrammetry, SRTM, Copernicus DEM.
  • Slope: Maximum rate of elevation change per unit distance (degrees or percent). Core input for erosion, landslide, and routing models.
  • Aspect: Compass direction a slope faces (0–360°, clockwise from north). Flat cells are assigned –1. Controls solar insolation and moisture.
  • Curvature: Rate of change of slope. Profile curvature describes flow acceleration/deceleration; plan curvature describes flow convergence/divergence.
  • TPI / geomorphons: Terrain position indices and landform classification assign cells to ridge, slope, valley, etc., without manual thresholds.
  • TWI: Topographic Wetness Index — ln(upslope area / tan(slope)) — predicts persistent soil moisture and runoff zones.

End-to-End Workflow: Primary Terrain Derivatives

This workflow takes a raw DEM through sink filling, then derives the most commonly used terrain surfaces.

Inputs

LayerFormatNotes
dem.tifGeoTIFF rasterProjected CRS (e.g. UTM) strongly recommended

Step 1 — Fill Depressions

Sinks (isolated low cells) in a DEM cause flow-routing artifacts in all downstream terrain derivatives. Fill them first.

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → Fill Depressions

ParameterRecommended value
Input DEMdem.tif
Fix flats✓ enabled
Flat increment0.001 (one thousandth of the DEM z unit)
Outputdem_filled.tif

Why fix flats? Perfectly flat areas produce ambiguous flow directions. Adding a tiny gradient across flats ensures a routable surface.


Step 2 — Slope

Processing Toolbox → Whitebox Workflows → Terrain Analysis → Slope

ParameterRecommended value
Input DEMdem_filled.tif
Output unitsDegrees
Z conversion factor1.0 (set to 0.3048 if DEM z is in feet but CRS is metres)
Outputslope.tif

Expected output range: 0° (flat) to ~85° (near-vertical cliff). Values above 70° often indicate interpolation artefacts — inspect those cells.


Step 3 — Aspect

Processing Toolbox → Whitebox Workflows → Terrain Analysis → Aspect

ParameterRecommended value
Input DEMdem_filled.tif
Outputaspect.tif

Flat cells receive –1. Apply a pseudocolor ramp (circular HSV) to aspect for intuitive visualisation of slope direction.


Step 4 — Hillshade

Processing Toolbox → Whitebox Workflows → Terrain Analysis → Hillshade

ParameterRecommended value
Input DEMdem_filled.tif
Azimuth (°)315 (NW sun — standard cartographic convention)
Altitude (°)45
Z factor1.0
Outputhillshade.tif

Set the hillshade layer to Multiply blend mode in QGIS and overlay it on a coloured DEM for a publication-quality relief map.


Step 5 — Profile and Plan Curvature

Processing Toolbox → Whitebox Workflows → Terrain Analysis → Profile Curvature

ParameterRecommended value
Input DEMdem_filled.tif
Outputprofile_curv.tif

Repeat for Plan Curvatureplan_curv.tif.

Style both outputs with a diverging colour ramp centred on 0. Negative profile curvature (blue) marks deceleration zones (valley bottoms); positive (red) marks acceleration zones (ridge crests). Plan curvature negatives mark convergent hollows; positives mark divergent noses.


Step 6 — Topographic Wetness Index

Processing Toolbox → Whitebox Workflows → Terrain Analysis → Wetness Index

ParameterRecommended value
Slope rasterslope.tif
Specific contributing area raster(run D8 Flow Accumulation first — see Spatial Hydrology chapter)
Outputtwi.tif

High TWI values (> 8–10) indicate persistent moisture zones. Use as a predictor variable in soil, flood, and habitat models.


Python Console Equivalent

Paste the following into the QGIS Python Console (Plugins → Python Console) or save as a Processing script to batch multiple DEMs.

import processing

dem = '/data/dem.tif'

# Step 1: fill depressions
processing.run('whitebox_workflows:fill_depressions', {
    'dem': dem,
    'fix_flats': True,
    'flat_increment': 0.001,
    'output': '/data/dem_filled.tif',
})

# Step 2: slope
processing.run('whitebox_workflows:slope', {
    'dem': '/data/dem_filled.tif',
    'units': 'Degrees',
    'zfactor': 1.0,
    'output': '/data/slope.tif',
})

# Step 3: aspect
processing.run('whitebox_workflows:aspect', {
    'dem': '/data/dem_filled.tif',
    'output': '/data/aspect.tif',
})

# Step 4: hillshade
processing.run('whitebox_workflows:hillshade', {
    'dem': '/data/dem_filled.tif',
    'azimuth': 315.0,
    'altitude': 45.0,
    'zfactor': 1.0,
    'output': '/data/hillshade.tif',
})

# Step 5: curvature
processing.run('whitebox_workflows:profile_curvature', {
    'dem': '/data/dem_filled.tif',
    'output': '/data/profile_curv.tif',
})
processing.run('whitebox_workflows:plan_curvature', {
    'dem': '/data/dem_filled.tif',
    'output': '/data/plan_curv.tif',
})

print("Terrain derivatives complete.")

Advanced: Geomorphons Landform Classification

Geomorphons classify each cell into one of ten landform elements (peak, ridge, shoulder, spur, slope, hollow, footslope, valley, pit, flat) by analysing horizon profiles in eight compass directions.

Processing Toolbox → Whitebox Workflows → Terrain Analysis → Geomorphons

ParameterRecommended value
Input DEMdem_filled.tif
Search distance (cells)50 (adjust to DEM resolution and landscape scale)
Skip radius (cells)0
Flatness threshold (°)1.0
Outputgeomorphons.tif

The output is a categorical raster (1–10). Apply a predefined categorical colour map — the Geomorphons palette is available in many QGIS style repositories.

processing.run('whitebox_workflows:geomorphons', {
    'dem': '/data/dem_filled.tif',
    'search': 50,
    'skip': 0,
    'threshold': 1.0,
    'output': '/data/geomorphons.tif',
})

Common Pitfalls

ProblemLikely causeFix
Slope values are unrealistically high (> 85°)DEM has interpolation artefacts or NoData spikesRun Remove Off-terrain Objects or inspect raw DEM
Flat areas produce zero slope everywhereDepressions not filled before slope derivationRun Fill Depressions first
Aspect shows –1 across large areasLarge flat regions in DEMExpected for flat input; check DEM resolution
Curvature is noisy on fine-resolution DEMsSensor noise dominates at small spatial scalesApply Gaussian Filter (σ ≈ 1–2 cells) before curvature
Units mismatch — Z factor warningHorizontal CRS in metres but DEM z in feetSet Z conversion factor to 0.3048

Validation Checklist

  • DEM uses a projected CRS (not geographic degrees).
  • No unexpected flat artefacts introduced by depression filling.
  • Slope range plausible for local relief (inspect histogram).
  • Aspect –1 cells are spatially limited to genuine flats.
  • Curvature raster has a near-symmetric distribution centred on 0.
  • Hillshade visually matches known ridgelines and valley geometry.

Spatial Hydrology

Spatial hydrology workflows extract hydrographic structure — flow directions, stream networks, watersheds, and hydrologic indices — from a terrain model. All hydrologic processing begins with a hydrologically conditioned DEM. This chapter demonstrates a complete watershed delineation workflow from raw DEM to labelled catchments.


Key Concepts

  • Hydrologic conditioning: Removing or breaching depressions so that flow can route from every cell to a basin outlet without interruption.
  • Flow direction: Per-cell pointer indicating which of the eight cardinal and diagonal neighbours receives runoff (D8 model) or a fractional multi-direction model (D-infinity, MD8).
  • Flow accumulation: Upslope contributing area (in cells or area units). High values mark channels; low values mark ridges.
  • Stream extraction threshold: Minimum contributing area that defines a first-order channel. Smaller thresholds produce denser networks.
  • Watershed / catchment: All cells draining to a common outlet. Delineated by tracing flow direction upstream from outlet points.
  • Strahler order: Hierarchical stream ordering from headwaters (order 1) to main channel (highest order). Used to characterise drainage network complexity.

End-to-End Workflow: Watershed Delineation

Inputs

LayerFormatNotes
dem.tifGeoTIFF rasterProjected CRS, metres
outlets.shpPoint vectorOne or more pour points

Step 1 — Breach Depressions (Preferred Conditioning Method)

Breaching cuts a narrow channel through depression rims rather than filling them, preserving more of the original topography.

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → Breach Depressions (Least Cost)

ParameterRecommended value
Input DEMdem.tif
Maximum search distance (cells)10
Maximum breach depth2.0 (metres)
Flat increment0.001
Fill remaining depressions✓ enabled
Outputdem_conditioned.tif

If the DEM has large lake or wetland depressions that should not be breached, use Fill Depressions instead.


Step 2 — D8 Flow Direction

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → D8 Pointer

ParameterRecommended value
Input DEMdem_conditioned.tif
Outputd8_pointer.tif

The output is an integer raster (powers of 2: 1, 2, 4, 8, 16, 32, 64, 128) encoding the direction to the steepest downslope neighbour.


Step 3 — D8 Flow Accumulation

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → D8 Flow Accumulation

ParameterRecommended value
Input D8 pointerd8_pointer.tif
Output typeCells
Log-transform output☐ (disable for threshold-based channel extraction)
Outputd8_accum.tif

Visualise with a logarithmic stretch. The highest values form the main channel network.


Step 4 — Extract Stream Network

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → Extract Streams

ParameterRecommended value
Flow accumulation rasterd8_accum.tif
Threshold500 (cells — adjust for DEM resolution and drainage density)
Zero background✓ enabled
Outputstreams.tif

Rule of thumb: for a 10 m DEM, a threshold of 500 cells ≈ 0.05 km² contributing area, producing a moderately dense first-order network. Halve or double the threshold to adjust density.

Convert to vector for display: Processing → Raster Streams to Vectorstreams.shp.


Step 5 — Snap Pour Points

Pour points must sit on the channel raster. Snap them to the nearest high-accumulation cell to avoid off-channel watershed boundaries.

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → Snap Pour Points

ParameterRecommended value
Pour pointsoutlets.shp
Flow accumulationd8_accum.tif
Snap distance (map units)200 (metres — adjust to point accuracy)
Outputoutlets_snapped.shp

Step 6 — Watershed Delineation

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → Watershed

ParameterRecommended value
D8 pointerd8_pointer.tif
Pour pointsoutlets_snapped.shp
Outputwatersheds.tif

Each outlet receives a unique integer ID; cells are assigned that ID. Use Raster to Vector (Polygons) in QGIS to produce watershed boundary polygons, then dissolve by ID if multiple raster cells share an outlet.


Step 7 — Strahler Stream Order (Optional)

Processing Toolbox → Whitebox Workflows → Spatial Hydrology → Strahler Stream Order

ParameterRecommended value
D8 pointerd8_pointer.tif
Streams rasterstreams.tif
Outputstrahler.tif

Python Console Equivalent

import processing

dem = '/data/dem.tif'
outlets = '/data/outlets.shp'

# Step 1: condition DEM
processing.run('whitebox_workflows:breach_depressions_least_cost', {
    'dem': dem,
    'max_dist': 10,
    'max_depth': 2.0,
    'flat_increment': 0.001,
    'fill': True,
    'output': '/data/dem_conditioned.tif',
})

# Step 2: flow direction
processing.run('whitebox_workflows:d8_pointer', {
    'dem': '/data/dem_conditioned.tif',
    'output': '/data/d8_pointer.tif',
})

# Step 3: flow accumulation
processing.run('whitebox_workflows:d8_flow_accumulation', {
    'input': '/data/d8_pointer.tif',
    'output_type': 'Cells',
    'log': False,
    'output': '/data/d8_accum.tif',
})

# Step 4: extract streams
processing.run('whitebox_workflows:extract_streams', {
    'flow_accum': '/data/d8_accum.tif',
    'threshold': 500.0,
    'zero_background': True,
    'output': '/data/streams.tif',
})

# Step 5: snap pour points
processing.run('whitebox_workflows:snap_pour_points', {
    'pour_pts': outlets,
    'flow_accum': '/data/d8_accum.tif',
    'snap_dist': 200.0,
    'output': '/data/outlets_snapped.shp',
})

# Step 6: watershed
processing.run('whitebox_workflows:watershed', {
    'd8_pntr': '/data/d8_pointer.tif',
    'pour_pts': '/data/outlets_snapped.shp',
    'output': '/data/watersheds.tif',
})

print("Watershed delineation complete.")

Advanced: Topographic Wetness Index

TWI requires the specific catchment area (flow accumulation in area units per unit contour width) rather than the raw cell count.

# SCA-based flow accumulation
processing.run('whitebox_workflows:d8_flow_accumulation', {
    'input': '/data/d8_pointer.tif',
    'output_type': 'Specific Contributing Area',
    'log': False,
    'output': '/data/sca.tif',
})

# Slope in radians (required for TWI)
processing.run('whitebox_workflows:slope', {
    'dem': '/data/dem_conditioned.tif',
    'units': 'Radians',
    'output': '/data/slope_rad.tif',
})

# TWI
processing.run('whitebox_workflows:wetness_index', {
    'sca': '/data/sca.tif',
    'slope': '/data/slope_rad.tif',
    'output': '/data/twi.tif',
})

Common Pitfalls

ProblemLikely causeFix
Watershed does not extend to expected ridgelinePour point not on channel rasterRun Snap Pour Points before Watershed
Parallel flow stripes in accumulation rasterFlat areas in conditioned DEMEnable fix-flats during conditioning
Stream network is too sparse / too denseThreshold too high / too lowHalve or double threshold and re-inspect
Watershed covers entire DEMPour point is at or near the DEM outlet cellCheck that outlet coordinates fall inside the DEM extent
TWI has very high values in flat areasSlope is near-zero, causing division by tan(0)Mask flat areas or apply a minimum slope floor (e.g. 0.001 rad)

Validation Checklist

  • Conditioned DEM has no isolated flat areas (check flow direction raster for NoData).
  • Flow accumulation values increase monotonically toward basin outlet.
  • Extracted channels follow expected valley geometry in the DEM.
  • Snapped pour points lie on the highest-accumulation cells within snap distance.
  • Watershed boundary is a closed polygon that contains the pour point.
  • Strahler orders are consistent with tributary junctions.

LiDAR Processing

LiDAR point clouds are the highest-resolution elevation data source available for most practitioners. WbW-QGIS exposes the full Whitebox LiDAR pipeline — from quality assurance through ground classification, surface modelling, and height normalisation — directly in the QGIS Processing Toolbox.

This chapter walks through a complete bare-earth and canopy-height workflow starting from a raw LAS/LAZ file.


Key Concepts

  • Point cloud: A set of 3-D coordinates (X, Y, Z) plus attributes (intensity, return number, classification, scan angle, etc.) acquired by laser scanning from airborne or terrestrial platforms.
  • LAS/LAZ: The industry-standard binary format for point clouds. LAZ is a losslessly compressed variant. COPC (Cloud-Optimised Point Cloud) is a tiled LAZ variant for efficient streaming.
  • Classification codes: Numeric labels assigned to points indicating surface type (1 = unclassified, 2 = ground, 3–5 = vegetation, 6 = building, etc. per ASPRS convention).
  • DTM: Digital Terrain Model — a raster surface interpolated from ground-classified points only.
  • DSM: Digital Surface Model — a raster surface from first returns, representing the tops of all objects (vegetation, buildings).
  • CHM: Canopy Height Model — DSM minus DTM, representing object height above ground.
  • Height above ground (HAG): Per-point elevation relative to the interpolated ground surface. Enables classification of vegetation returns by height tier.

End-to-End Workflow: DTM, DSM, and Canopy Height Model

Inputs

LayerFormatNotes
cloud.lazLAZ point cloudAny ASPRS LAS version 1.0–1.4

Step 1 — Point Cloud Quality Check

Processing Toolbox → Whitebox Workflows → LiDAR → LiDAR Point Stats

ParameterRecommended value
Input LiDAR filecloud.laz
Outputcloud_stats.html (HTML report)

Review the report for:

  • Point density (pts/m²)
  • Classification distribution (% ground, vegetation, unclassified)
  • Z range and intensity histogram
  • Scan angle range (should be ±20° for most airborne missions)

Step 2 — Thin High-Density Files (Optional)

For files > 50 pts/m², thinning reduces processing time with minimal accuracy loss.

Processing Toolbox → Whitebox Workflows → LiDAR → LiDAR Thin

ParameterRecommended value
Input LiDAR filecloud.laz
Resolution0.5 (metres)
Retain ground points✓ enabled
Outputcloud_thin.laz

Step 3 — Classify Ground Points

If the input file has all points as unclassified (class 1), classify ground returns before surface modelling.

Processing Toolbox → Whitebox Workflows → LiDAR → LiDAR Ground Point Filter

ParameterRecommended value
Input LiDAR filecloud.laz (or cloud_thin.laz)
Radius (m)2.0
Minimum slope (°)5.0
Maximum slope (°)85.0
Terrain typeNormal
Outputcloud_classified.laz

For complex terrain (steep slopes, dense vegetation), increase radius to 4.0 and reduce minimum slope to 2.0.


Step 4 — Build DTM from Ground Points

Processing Toolbox → Whitebox Workflows → LiDAR → LiDAR IDW Interpolation

ParameterRecommended value
Input LiDAR filecloud_classified.laz
IDW weight2.0
Search radius (m)2.5
Minimum number of points3
Exclusion classes(leave empty to use ground points only)
ReturnsLast
Point classes included2 (ground)
Grid resolution0.5
Outputdtm.tif

Alternatively, use LiDAR TIN Gridding for faster interpolation on uniformly distributed clouds.


Step 5 — Build DSM from First Returns

Processing Toolbox → Whitebox Workflows → LiDAR → LiDAR IDW Interpolation (second pass)

ParameterRecommended value
Input LiDAR filecloud_classified.laz
ReturnsFirst
Point classes included(all — leave blank)
Grid resolution0.5
Outputdsm.tif

Step 6 — Canopy Height Model

Subtract DTM from DSM using the QGIS Raster Calculator, or use the dedicated CHM tool.

Processing Toolbox → Whitebox Workflows → LiDAR → Canopy Height Model

ParameterRecommended value
Input LiDAR filecloud_classified.laz
DTM rasterdtm.tif
Grid resolution0.5
Outputchm.tif

Apply a stretch from 0 to the 98th percentile height value. Negative cells (< 0) indicate DTM–DSM interpolation artefacts; clamp to 0 in post-processing.


Step 7 — Height Above Ground Normalisation

Assign a HAG value to every point for per-return vertical stratification analysis.

Processing Toolbox → Whitebox Workflows → LiDAR → Height Above Ground

ParameterRecommended value
Input LiDAR filecloud_classified.laz
Outputcloud_hag.laz

The tool sets the Z coordinate of each point to its height above the interpolated ground surface. Ground points are set to 0.


Python Console Equivalent

import processing

cloud = '/data/cloud.laz'

# Step 1: stats / QA
processing.run('whitebox_workflows:lidar_point_stats', {
    'input': cloud,
    'output': '/data/cloud_stats.html',
})

# Step 3: ground classification
processing.run('whitebox_workflows:lidar_ground_point_filter', {
    'input': cloud,
    'radius': 2.0,
    'min_slope': 5.0,
    'max_slope': 85.0,
    'terrain_type': 'Normal',
    'output': '/data/cloud_classified.laz',
})

# Step 4: DTM
processing.run('whitebox_workflows:lidar_idw_interpolation', {
    'input': '/data/cloud_classified.laz',
    'parameter': 'elevation',
    'returns': 'Last',
    'classes_included': '2',
    'weight': 2.0,
    'radius': 2.5,
    'min_points': 3,
    'resolution': 0.5,
    'output': '/data/dtm.tif',
})

# Step 5: DSM
processing.run('whitebox_workflows:lidar_idw_interpolation', {
    'input': '/data/cloud_classified.laz',
    'parameter': 'elevation',
    'returns': 'First',
    'classes_included': '',
    'weight': 2.0,
    'radius': 2.5,
    'min_points': 3,
    'resolution': 0.5,
    'output': '/data/dsm.tif',
})

# Step 6: CHM
processing.run('whitebox_workflows:canopy_height_model', {
    'input': '/data/cloud_classified.laz',
    'dtm': '/data/dtm.tif',
    'resolution': 0.5,
    'output': '/data/chm.tif',
})

# Step 7: HAG
processing.run('whitebox_workflows:height_above_ground', {
    'input': '/data/cloud_classified.laz',
    'output': '/data/cloud_hag.laz',
})

print("LiDAR pipeline complete.")

Common Pitfalls

ProblemLikely causeFix
DTM has large NoData holesGround point density too lowIncrease IDW search radius or use TIN gridding
CHM has negative valuesDTM higher than DSM in flat/water areasClamp CHM ≥ 0 with Raster Calculator after generation
Classification codes all zero after processingInput was LAS point format 6–10 and legacy writer bug (see known issues)Use WbW Next Gen pipeline — classification is preserved correctly
Ground filter over-segments in steep terrainSlope parameters too restrictiveIncrease max slope to 85° and radius to 4 m
LiDAR stats report extreme Z valuesOutlier high/low points presentRun LiDAR Remove Outliers before classification

Validation Checklist

  • Point stats report shows expected classification distribution (> 5 % ground points).
  • DTM has no large NoData holes in vegetated areas.
  • DTM is smooth with no pits deeper than 1–2 m.
  • DSM ≥ DTM across the entire overlap extent.
  • CHM values are 0 on roads and bare ground.
  • HAG-normalised cloud has all ground points at Z ≈ 0 (±0.1 m).

Remote Sensing Analysis

Remote sensing workflows in WbW-QGIS cover multispectral and hyperspectral image analysis: spectral index computation, image enhancement, principal component analysis (PCA), segmentation, and change detection.

This chapter is aligned with the Python and R manuals while staying focused on QGIS Processing Toolbox execution patterns.


Core Concepts You Should Know First

  • Spectral bands: Wavelength-specific image channels (for example blue, red, NIR, SWIR) used to separate land-cover materials.
  • Spectral indices: Band combinations that highlight specific targets, such as NDVI (vegetation), NDWI (water), and NBR (burn severity).
  • Spatial resolution: Pixel size affects detail and detectability of features.
  • Temporal resolution: Revisit interval controls change-detection sensitivity.
  • Atmospheric and cloud effects: Compare like-with-like by masking clouds and shadows and using corrected imagery where possible.
  • Change detection: Compare index or class outputs across acquisition dates.
  • Dimensionality reduction: PCA reduces band redundancy before segmentation or classification.

Typical Inputs

LayerFormatNotes
image_t1.tifMultiband GeoTIFFTime-1 scene, reflectance preferred
image_t2.tifMultiband GeoTIFFTime-2 scene, same sensor/preprocessing
cloud_mask_t1.tifRasterOptional cloud or QA-derived mask
cloud_mask_t2.tifRasterOptional cloud or QA-derived mask

End-to-End Workflow

Step 1 - Quality Check and Harmonize Inputs

Before analysis:

  • Confirm both scenes use the same CRS, grid, and pixel size.
  • Confirm band order and data scale (for example 0-1 reflectance or scaled integer reflectance).
  • Mask clouds/shadows using QA products or Raster Calculator conditions.

Use QGIS Raster menu tools as needed:

  • Align Raster
  • Warp (Reproject)
  • Raster Calculator

Step 2 - Build Key Spectral Indices

Process both dates with the same settings.

Processing Toolbox -> Whitebox Workflows -> Remote Sensing:

  • NDVI
  • Normalized Difference Index (for NDWI, NBR, NDSI, NDBI patterns)

Recommended outputs:

  • ndvi_t1.tif, ndvi_t2.tif
  • ndwi_t1.tif, ndwi_t2.tif
  • nbr_t1.tif, nbr_t2.tif

Example NDVI run:

ParameterValue
Input imageimage_t1.tif
NIR bandsensor-specific (for example 4, 5, or 8 depending on product)
Red bandsensor-specific
Outputndvi_t1.tif

Repeat for time 2.


Step 3 - Create Change Surfaces

Use QGIS Raster Calculator for differencing:

  • NDVI change: ndvi_t2 - ndvi_t1
  • NBR change: nbr_t2 - nbr_t1

Then classify into practical bins (loss, stable, gain) using:

  • Whitebox Workflows -> Raster Analysis -> Reclass
  • or Raster Calculator threshold expressions

Suggested interpretation for NDVI change:

ClassThreshold
Strong loss< -0.20
Moderate loss-0.20 to -0.10
Stable-0.10 to 0.10
Moderate gain0.10 to 0.20
Strong gain> 0.20

Step 4 - Dimensionality Reduction (PCA)

Processing Toolbox -> Whitebox Workflows -> Remote Sensing -> Principal Component Analysis

Use PCA when:

  • Bands are highly correlated.
  • You need compact inputs for segmentation or clustering.
  • You are preparing a classification feature stack.

Inspect output variance/eigenvalue diagnostics and retain only the components needed for most variance.


Step 5 - Segmentation and Classification Prep

Processing Toolbox -> Whitebox Workflows -> Remote Sensing -> Image Segmentation

Typical tuning:

  • Lower threshold -> more, smaller segments
  • Higher threshold -> fewer, larger segments
  • Minimum segment size removes speckle

After segmentation:

  • Optionally polygonize segment rasters in QGIS.
  • Join zonal metrics from index layers.
  • Use resulting segment features for training/validation workflows.

QGIS Python Console Equivalent

Use this pattern for reproducible batch processing in QGIS:

import processing

img_t1 = '/data/image_t1.tif'
img_t2 = '/data/image_t2.tif'

for label, img in [('t1', img_t1), ('t2', img_t2)]:
    processing.run('whitebox_workflows:ndvi', {
        'input': img,
        'nir_band': 4,
        'red_band': 3,
        'output': f'/data/ndvi_{label}.tif',
    })

processing.run('qgis:rastercalculator', {
    'EXPRESSION': '"ndvi_t2@1" - "ndvi_t1@1"',
    'LAYERS': ['/data/ndvi_t1.tif', '/data/ndvi_t2.tif'],
    'OUTPUT': '/data/ndvi_change.tif',
})

processing.run('whitebox_workflows:principal_component_analysis', {
    'input': '/data/image_t1.tif',
    'num_comp': 4,
    'output': '/data/pca_t1.tif',
})

Common Pitfalls

ProblemLikely causeFix
Index values are outside expected rangesWrong bands or scale mismatchVerify band mapping and value scale
Apparent change is mostly cloud edgesMissing cloud/shadow maskingMask QA classes before differencing
PCA output looks unstableNoData included in statsMask NoData consistently
Segmentation over-merges featuresThreshold too highLower threshold and retest
Change map is noisyDifferent spatial grids or radiometryAlign rasters and normalize radiometry

Validation Checklist

  • Inputs are co-registered and in a common CRS.
  • Band assignments match sensor metadata.
  • Cloud/shadow/no-data masking applied consistently across dates.
  • Index histograms are plausible for local land cover.
  • Change classes were reviewed visually against source imagery.
  • PCA/segmentation parameters were documented for reproducibility.

Raster Analysis

General raster analysis covers a broad set of cell-based operations: local algebra on single rasters, focal statistics across neighbourhoods, zonal summaries within polygon regions, reclassification, and suitability scoring. These tools form the computational backbone of many GIS modelling workflows.

This chapter demonstrates an environmental suitability analysis that combines multiple raster datasets through reclassification and weighted overlay.


Key Concepts

  • Local operations: Applied to each cell independently — arithmetic, logic, trigonometry, conditional assignment. Result depends only on the cell's own value (and corresponding cells in other input rasters).
  • Focal operations: Applied to each cell using values within a spatial neighbourhood (kernel). Examples: focal mean, focal max, focal standard deviation.
  • Zonal statistics: Aggregate raster values by zone boundaries defined by a second raster or vector polygon layer.
  • Reclassification: Map old cell values to new values via a lookup table or range intervals. Core step in suitability and habitat modelling.
  • Weighted overlay: Combine multiple reclassified factor rasters using factor-specific weights. The weighted sum produces a composite suitability score.
  • NoData / null handling: Cells with NoData propagate through most local operations. Ensure all input rasters share the same extent, resolution, and NoData mask before combining them.

End-to-End Workflow: Multi-Criteria Habitat Suitability

This workflow scores terrain for habitat suitability using slope, TWI, and distance from water as factors.

Inputs

LayerFormatNotes
slope.tifGeoTIFF rasterDegrees, from terrain analysis
twi.tifGeoTIFF rasterTopographic Wetness Index
streams.tifGeoTIFF rasterBinary stream network

All rasters must share the same projected CRS, extent, and cell size before being combined. Use Snap Raster Extents or QGIS Warp (Reproject) to align if needed.


Step 1 — Compute Distance from Water

Processing Toolbox → Whitebox Workflows → GIS Analysis → Euclidean Distance

ParameterRecommended value
Input featurestreams.tif
Outputdist_water.tif

Cells closer to water receive smaller values. This will be reclassified so that proximity = higher suitability.


Step 2 — Reclassify Slope Factor

Assign suitability scores 1–5 to slope ranges.

Processing Toolbox → Whitebox Workflows → Raster Analysis → Reclass

ClassSlope range (°)Suitability score
1> 301 (unsuitable)
220–302
310–203
45–104
50–55 (most suitable)

Use Reclass From File with a two-column table file (old value; new value) or set up intervals in the tool dialogue.

ParameterRecommended value
Input rasterslope.tif
Reclass intervals fileslope_reclass.txt
Outputslope_reclass.tif

Step 3 — Reclassify TWI Factor

ClassTWI rangeSuitability score
1< 41
24–62
36–83
48–104
5> 105
Processing Toolbox → Whitebox Workflows → Raster Analysis → Reclass
Input: twi.tif → Output: twi_reclass.tif

Step 4 — Reclassify Distance from Water

Closer = more suitable:

ClassDistance range (m)Suitability score
1> 5001
2300–5002
3100–3003
450–1004
50–505
Processing Toolbox → Whitebox Workflows → Raster Analysis → Reclass
Input: dist_water.tif → Output: dist_water_reclass.tif

Step 5 — Weighted Overlay (Raster Calculator)

Combine the three factors using assigned weights (must sum to 1.0).

FactorWeight
Slope suitability0.4
TWI suitability0.35
Distance suitability0.25

QGIS Raster Calculator:

("slope_reclass@1" * 0.4) + ("twi_reclass@1" * 0.35) + ("dist_water_reclass@1" * 0.25)

Output: suitability.tif (range 1–5, continuous).


Step 6 — Zonal Statistics (Optional)

Summarise suitability scores by catchment polygon.

Processing Toolbox → QGIS → Vector Analysis → Zonal Statistics (QGIS native)

ParameterRecommended value
Input rastersuitability.tif
Vector layerwatersheds.shp
StatisticsMean, Max, Std Dev
Output column prefixsuit_

Python Console Equivalent

import processing

# Step 1: distance from water
processing.run('whitebox_workflows:euclidean_distance', {
    'input': '/data/streams.tif',
    'output': '/data/dist_water.tif',
})

# Step 2–4: reclassify each factor
for src, dst in [
    ('slope', 'slope_reclass'),
    ('twi', 'twi_reclass'),
    ('dist_water', 'dist_water_reclass'),
]:
    processing.run('whitebox_workflows:reclass_from_file', {
        'input': f'/data/{src}.tif',
        'reclass_vals': f'/data/{src}_reclass.txt',
        'output': f'/data/{dst}.tif',
    })

# Step 5: weighted overlay via Raster Calculator
processing.run('qgis:rastercalculator', {
    'EXPRESSION': '("slope_reclass@1" * 0.4) + ("twi_reclass@1" * 0.35) + ("dist_water_reclass@1" * 0.25)',
    'LAYERS': [
        '/data/slope_reclass.tif',
        '/data/twi_reclass.tif',
        '/data/dist_water_reclass.tif',
    ],
    'OUTPUT': '/data/suitability.tif',
})

print("Suitability analysis complete.")

Advanced: Focal Statistics

Focal statistics smooth or enhance spatial patterns at a neighbourhood scale.

Processing Toolbox → Whitebox Workflows → Raster Analysis → Mean Filter

ParameterRecommended value
Input rastersuitability.tif
Filter size X5 (cells)
Filter size Y5
Outputsuitability_smooth.tif

Use Standard Deviation Filter to highlight areas of high local variability, or Percentile Filter for rank-based neighbourhood smoothing.


Common Pitfalls

ProblemLikely causeFix
Raster Calculator outputs all NoDataRasters have different extents or CRSClip/warp all inputs to common grid before combining
Reclass produces unexpected valuesRange gaps or overlaps in reclass tableVerify that intervals are contiguous with no gap or overlap
Zonal statistics returns wrong polygon countsRaster–vector CRS mismatchReproject vector to match raster CRS before running
Weighted overlay result > 5Weights do not sum to 1.0Recalculate weights so they sum to exactly 1.0
Focal filter introduces edge NoDataKernel extends beyond raster boundaryPad raster with Expand Raster before filtering, or ignore edge cells

Validation Checklist

  • All input rasters aligned (same CRS, extent, cell size, NoData value).
  • Reclass table covers the full observed value range with no gaps.
  • Weighted overlay weights sum to 1.0.
  • Output suitability range matches expected 1–5 interval.
  • Zonal statistics polygon CRS matches raster CRS.
  • Focal filter kernel size is appropriate for target feature scale.

Vector Analysis

Vector analysis in WbW-QGIS covers geometry validation, overlay operations, attribute enrichment, spatial selection, proximity analysis, and spatial joining. Whitebox supplements the native QGIS vector toolbox with high-performance tools built on the wbtopology spatial index.

This chapter walks through a complete parcel-attribute enrichment workflow — a common task in land management and environmental assessment.


Key Concepts

  • Geometry validity: Self-intersecting rings, duplicate vertices, and unclosed polygons cause silent failures in overlay tools. Always validate and repair geometry before any overlay operation.
  • Spatial join: Assigns attributes from one layer to features in another based on spatial relationship (intersects, contains, nearest). Supports aggregation modes (first, last, sum, mean, count, min, max).
  • Near analysis: Finds the nearest feature (or features within a distance) from a source layer to a target layer. Returns distance and optional target attributes.
  • Clip / Intersection / Difference: Standard polygon overlay operations. Clip retains the geometry of input A bounded by B. Intersection produces the geometric overlap. Difference removes the overlap.
  • Add geometry attributes: Computes and appends area, perimeter, length, centroid coordinates, or bounding box dimensions as new attribute fields.
  • Select by location: Spatial predicate query (intersects, within, contains, etc.) that produces a feature selection or a new filtered layer.

End-to-End Workflow: Parcel Attribute Enrichment

This workflow assigns catchment statistics and proximity-to-road measurements to a parcel layer.

Inputs

LayerFormatNotes
parcels.shpPolygon vectorLand parcel boundaries
catchments.shpPolygon vectorWatershed polygons with area and slope stats
roads.shpPolyline vectorRoad network

Step 1 — Validate and Repair Geometry

Processing Toolbox → Vector Geometry → Fix Geometries (QGIS native)

ParameterRecommended value
Input layerparcels.shp
Outputparcels_valid.shp

Run Check Validity on both catchments.shp and roads.shp and fix any errors before proceeding.


Step 2 — Add Geometry Attributes to Parcels

Processing Toolbox → Whitebox Workflows → Vector Analysis → Add Geometry Attributes

ParameterRecommended value
Input vectorparcels_valid.shp
UnitsMetres
Outputparcels_geom.shp

This appends AREA, PERIMETER, and centroid X/Y fields to each parcel.


Step 3 — Spatial Join: Assign Catchment Attributes to Parcels

Processing Toolbox → Whitebox Workflows → Vector Analysis → Spatial Join

ParameterRecommended value
Target layerparcels_geom.shp
Join layercatchments.shp
Spatial relationshipIntersects
Join strategyFirst (largest overlap catchment)
Fields to joincatch_id, mean_slope, area_km2
Outputparcels_joined.shp

Each parcel now carries the attributes of the catchment it intersects most.


Step 4 — Near: Distance from Each Parcel to Nearest Road

Processing Toolbox → Whitebox Workflows → Vector Analysis → Near

ParameterRecommended value
Input vector (source)parcels_joined.shp
Near vector (target)roads.shp
Max search distance (m)5000 (0 = search all)
Outputparcels_near.shp

Appended fields: NEAR_DIST (metres to nearest road segment), NEAR_FID (FID of nearest road feature).


Step 5 — Select High-Priority Parcels

Processing Toolbox → Whitebox Workflows → Vector Analysis → Select By Attribute or use QGIS Select by Expression:

"AREA" > 10000 AND "NEAR_DIST" < 500 AND "mean_slope" < 10

Export the selection as priority_parcels.shp using Layer → Export → Save Selected Features As.


Step 6 — Clip Parcels to Study Area (Optional)

Processing Toolbox → Whitebox Workflows → Vector Analysis → Line Polygon Clip (for lines) or QGIS Clip for polygon-on-polygon.

ParameterRecommended value
Input vectorpriority_parcels.shp
Clip polygonstudy_area.shp
Outputpriority_parcels_clipped.shp

TopoJSON Conversion Chain (QGIS Interop)

Use this workflow when you need to exchange shared-boundary vector data with web clients while keeping a GeoPackage working copy for analysis.

Inputs

LayerFormatNotes
zones.gpkgPolygon vectorAuthoritative analysis dataset

Step 1 — Run a Whitebox vector operation and emit TopoJSON

Processing Toolbox → Whitebox Workflows → Vector Analysis → Add Geometry Attributes

ParameterRecommended value
Input vectorzones.gpkg
UnitsMetres
Outputzones_metrics.topojson

This confirms the plugin accepts .topojson output targets in a normal vector processing chain.

Step 2 — Re-open TopoJSON and convert back to GeoPackage

  1. Add zones_metrics.topojson to the QGIS project.
  2. Right-click the layer, then choose Export → Save Features As....
  3. Set format to GeoPackage and save as zones_metrics_roundtrip.gpkg.

Step 3 — Validate roundtrip integrity

Check these before publishing or reusing the roundtrip layer:

  • Feature count matches source layer.
  • Core attributes (e.g., ID fields and geometry metrics) are preserved.
  • CRS is correctly populated on the roundtrip GeoPackage.
  • Keep .gpkg as the editable analysis master.
  • Generate .topojson as interchange or web-delivery artifacts.
  • Re-import to .gpkg for heavier downstream spatial analysis.

TopoJSON Boundary-Preserving Generalization Chain

Use this chain when you need smaller delivery payloads while preserving shared boundary consistency during simplification.

Inputs

LayerFormatNotes
admin_units.gpkgPolygon vectorShared boundaries between adjacent polygons

Step 1 — Simplify and emit TopoJSON

Processing Toolbox → Whitebox Workflows → Vector Analysis → Simplify Features

ParameterRecommended value
Input vectoradmin_units.gpkg
AlgorithmDouglas-Peucker
Tolerance25.0 (adjust to target scale)
Outputadmin_units_simplified.topojson

Step 2 — Inspect topology consistency in QGIS

  1. Add admin_units_simplified.topojson to the map.
  2. Inspect shared boundaries at high zoom for slivers/gaps.
  3. Validate feature count versus source before publication.

Step 3 — Export analysis copy

Export to admin_units_simplified.gpkg for downstream joins/overlay work.

TopoJSON Transport + Enrichment Return Chain

Use this chain when TopoJSON is used only for transport and you need to return to an analysis-grade format for attribute enrichment.

Inputs

LayerFormatNotes
transport_in.topojsonTopology-preserving vectorInterchange input received from external system

Step 1 — Convert transport input to GeoPackage

  1. Add transport_in.topojson to the project.
  2. Export as transport_stage.gpkg.

Step 2 — Apply enrichment tools

Run Whitebox vector tools against transport_stage.gpkg:

  • Add Geometry Attributes for geometry metrics.
  • Spatial Join for contextual attribute enrichment.
  • Near for proximity attributes.

Step 3 — Emit deliverables

Write two outputs:

  • transport_enriched.gpkg for analytic persistence.
  • transport_enriched.topojson for interchange/web handoff.

Python Console Equivalent

import processing

# Step 1: fix geometry
processing.run('native:fixgeometries', {
    'INPUT': '/data/parcels.shp',
    'OUTPUT': '/data/parcels_valid.shp',
})

# Step 2: add geometry attributes
processing.run('whitebox_workflows:add_geometry_attributes', {
    'input': '/data/parcels_valid.shp',
    'units': 'Metres',
    'output': '/data/parcels_geom.shp',
})

# Step 3: spatial join
processing.run('whitebox_workflows:spatial_join', {
    'input': '/data/parcels_geom.shp',
    'join': '/data/catchments.shp',
    'spatial_relation': 'Intersects',
    'join_method': 'First',
    'output': '/data/parcels_joined.shp',
})

# Step 4: near
processing.run('whitebox_workflows:near', {
    'input': '/data/parcels_joined.shp',
    'near': '/data/roads.shp',
    'max_dist': 5000.0,
    'output': '/data/parcels_near.shp',
})

print("Parcel enrichment complete.")

Advanced: Simplify Features for Cartographic Output

Large polygon datasets with many vertices slow down rendering and tile export. Simplify geometries while preserving topology.

Processing Toolbox → Whitebox Workflows → Vector Analysis → Simplify Features

ParameterRecommended value
Input vectorpriority_parcels_clipped.shp
AlgorithmDouglas-Peucker
Tolerance (m)5.0 (adjust to display scale)
Outputparcels_simplified.shp
processing.run('whitebox_workflows:simplify_features', {
    'input': '/data/priority_parcels_clipped.shp',
    'algorithm': 'DouglasPeucker',
    'tolerance': 5.0,
    'output': '/data/parcels_simplified.shp',
})

Common Pitfalls

ProblemLikely causeFix
Spatial join returns no matchesCRS mismatch between target and join layersReproject both to the same CRS before joining
Near returns –1 for all distancesSearch distance too small for data extentIncrease max_dist or set to 0 for unlimited search
Add geometry attributes returns wrong areaLayer CRS is geographic (degrees)Reproject to a projected CRS (metres) first
Simplify removes valid narrow featuresTolerance too largeUse a smaller tolerance (< 1 m for cadastral data)
Select by location selects too many featuresPredicate too inclusive (intersects vs. within)Switch to Within or Contains for strict containment

Validation Checklist

  • All input layers pass geometry validity check.
  • All vector layers share the same projected CRS.
  • Spatial join result preserves original feature count (check attribute table row count).
  • NEAR_DIST values are plausible (inspect histogram).
  • Simplified geometry does not self-intersect at the chosen tolerance.
  • Attribute field names in output do not exceed shapefile 10-character limit.

Network Analysis

Network analysis in WbW-QGIS spans both transportation and hydrologic networks. This chapter is aligned with the Python and R manuals and now covers three common tracks:

  • Transportation routing and service areas
  • OD and nearest-facility analysis
  • Stream-network hierarchy and connectivity

Core Concepts You Should Know First

  • Network: A graph of edges (line segments) and nodes (junctions/endpoints).
  • Cost or impedance: Value minimized by routing (distance, minutes, or other weighted friction).
  • OD pair: Origin and destination used in path queries.
  • Service area: All network locations reachable under a cost budget.
  • Closest facility: Nearest destination by network cost, not straight-line distance.
  • Connectivity: Whether all required features are in connected components.
  • Directed network: Edge direction matters (one-way roads, downstream streams).

Typical Inputs

LayerFormatNotes
roads.shpPolyline vectorCleaned road centerlines
facilities.shpPoint vectorHospitals, depots, schools, etc.
demand_points.shpPoint vectorIncidents, customers, or population centroids
streams.tifRasterBinary stream raster for hydrologic hierarchy
d8_pointer.tifRasterD8 flow-direction raster

Workflow A: Transportation Network Preparation

Step 1 - Topology QA and Geometry Cleanup

Use standard QGIS cleanup first:

  • Check validity
  • Snap Geometries to Layer
  • Fix Geometries

Then enrich network attributes with Whitebox tools:

Processing Toolbox -> Whitebox Workflows -> Vector Analysis -> Add Geometry Attributes

This provides segment length fields needed for distance-based routing.

If travel-time routing is required, compute a time field such as:

  • TIME_MIN = LENGTH_M / SPEED_M_PER_MIN

using Field Calculator.


Step 2 - Build Cost-Aware Road Layer

Recommended fields:

  • LENGTH_M (meters)
  • SPEED_KMH (if available)
  • TIME_MIN (derived)
  • ONEWAY (optional directional control)

Use this prepared layer as the routing network for native QGIS algorithms.


Step 2.5 - Build Network Topology and Snap Points (Optional)

If your network lacks proper node structure or you need to snap facility/demand points to the network:

Processing Toolbox → Whitebox Workflows → Vector Analysis → Build Network Topology

ParameterValue
Input vectorroads_prepared.shp
Snap tolerance0.5
Outputroads_noded.shp
Output nodesnetwork_nodes.shp

Then snap your facilities and demand points:

Processing Toolbox → Whitebox Workflows → Vector Analysis → Snap Points to Network

ParameterValue
Network layerroads_noded.shp
Points layerfire_stations.shp
Snap distance50.0 (meters)
Outputfire_stations_snapped.shp

Output includes SNAP_DIST (offset to network) for diagnostics.


Workflow B: Routing, Service Areas, and Closest Facility

Step 3 - Shortest Path

Processing Toolbox -> Network Analysis:

  • Shortest Path (Point to Point)
  • Shortest Path (Layer to Point)
  • Shortest Path (Point to Layer)

Use the prepared cost field (distance or time) consistently.


Step 4 - Service Area (Isochrone)

Processing Toolbox -> Network Analysis -> Service Area (From Layer)

Recommended parameters:

ParameterExample
Network layerroads_prepared.shp
StrategyShortest
Start pointsfacilities.shp
Travel cost5.0 (minutes) or 3000 (meters)

Export output lines and optional polygons for reporting.


Step 5 - Closest Facility Pattern

Use Service Area and shortest-path tools together:

  • Build candidate facilities
  • Route demand points to nearest reachable facilities
  • Summarize cost by facility catchment

For large batches, run model-builder or Python Console loops.


Workflow C: OD-Style Batch Analysis in QGIS

QGIS does not provide a single OD matrix tool equivalent to the Python/R chapters, so the standard QGIS pattern is:

  • Iterate origins and destinations in batch
  • Run shortest path for each pair
  • Aggregate travel cost in an output table

Use this when you need accessibility summaries or assignment baselines directly inside QGIS projects.


Workflow D: Hydrologic Stream Networks

Hydrologic network tools remain an important part of network analysis and are included here as a dedicated sub-workflow rather than the entire chapter.

Step 6 - Stream Hierarchy

Processing Toolbox -> Whitebox Workflows -> Spatial Hydrology:

  • Strahler Stream Order
  • Shreve Stream Magnitude
  • Hack Stream Order

These tools characterize stream position and downstream accumulation.

Step 7 - Stream Vectorization

Processing Toolbox -> Whitebox Workflows -> Spatial Hydrology -> Raster Streams to Vector

Convert ordered stream rasters to vector lines for cartography and further network operations.


QGIS Python Console Equivalent

import processing

# Add geometry attributes for road cost preparation
processing.run('whitebox_workflows:add_geometry_attributes', {
    'input': '/data/roads.shp',
    'output': '/data/roads_prepared.shp',
})

# Service area from facilities
processing.run('native:serviceareafromlayer', {
    'INPUT': '/data/roads_prepared.shp',
    'STRATEGY': 0,
    'START_POINTS': '/data/facilities.shp',
    'TRAVEL_COST': 5.0,
    'OUTPUT_LINES': '/data/service_area_lines.shp',
    'OUTPUT': 'TEMPORARY_OUTPUT',
})

# Stream order
processing.run('whitebox_workflows:strahler_stream_order', {
    'd8_pntr': '/data/d8_pointer.tif',
    'streams': '/data/streams.tif',
    'output': '/data/strahler.tif',
})

Common Pitfalls

ProblemLikely causeFix
No route found between known-connected pointsTopology gaps or unsnapped endpointsRun snapping and revalidate connectivity
Service area too small or too largeCost units inconsistentKeep all costs in either meters or minutes
One-way streets ignoredDirection field not configuredVerify direction settings in network algorithm
Batch routing is slowUnnecessary repeated reprojection or heavy geometryPreprocess to common CRS and simplify where appropriate
Stream order appears uniformBad stream threshold or mismatched d8/stream rastersRebuild streams and ensure matching extent/grid

Validation Checklist

  • Routing network passes geometry validity and snapping checks.
  • Cost field units are consistent across all analyses.
  • Directionality assumptions are documented (directed vs undirected).
  • Service-area outputs were spot-checked against known travel behavior.
  • Stream-order outputs were checked at confluences.
  • Workflow parameters were saved in model or processing history.

Linear Referencing

Linear referencing (LRS) is a data model where features are located along routes by a measured distance from a known origin — rather than by absolute X/Y coordinates. It is the foundation of road/rail inventory, pipeline inspection data, accident records, and pavement condition databases.

WbW-QGIS provides tools for building measure fields on route networks, locating point and line events along routes, and exporting event geometries for spatial analysis.


Key Concepts

  • Route: A polyline feature with a unique, stable route identifier (ROUTE_ID) and a monotonically increasing measure value (M-value) along its length.
  • M-value: The accumulated distance (or time, or post number) from the route origin to each vertex. Stored as the M coordinate in an MZ geometry.
  • Event: A point or interval on a route located by one measure (point event) or two measures (line event: from-measure, to-measure).
  • Event table: A tabular record set with ROUTE_ID, MEASURE (point) or FROM_M/TO_M (line), and any associated attributes.
  • Dynamic segmentation: The process of converting event tables to geometry by interpolating measure positions along routes.
  • Calibration: Adjusting M-values to match real-world control points — for example, aligning stationing to kilometre posts.

End-to-End Workflow: Locating Inspection Events Along a Road Network

This workflow builds measure fields on a road network, then locates a set of field inspection points as events on their respective routes.

Inputs

LayerFormatNotes
roads.shpPolyline vectorRoad centrelines, unique ROUTE_ID field
inspections.csvCSV tableColumns: ROUTE_ID, CHAINAGE_M, CONDITION

Step 1 — Add Cumulative Distance (Measure) Field

WbW computes the cumulative distance from each route's start vertex to every subsequent vertex and writes it as the M coordinate.

Processing Toolbox → Whitebox Workflows → Vector Analysis → Add Geometry Attributes

ParameterRecommended value
Input vectorroads.shp
UnitsMetres
Outputroads_geom.shp

This step appends LENGTH to each segment. For building full route M-values use the QGIS Set M Value tool (from Geometry group) after merging segments by ROUTE_ID.

Alternative — set M values from a field:

Processing Toolbox → Vector Geometry → Set M Value (QGIS native)

ParameterRecommended value
Input layerroads.shp (merged per route)
M valueExpression: $length (QGIS expression — cumulative along merged route)
Outputroutes_m.shp

Route Calibration and Recalibration

Measures are only useful when anchored to real-world control points such as kilometre posts or survey stations. If your routes lack calibration or have been edited, use these tools to establish stable, field-verified measures.

Calibrate Routes from Control Points

Processing Toolbox → Whitebox Workflows → Linear Referencing → Route Calibrate

ParameterValue
Input routesroads.shp (with ROUTE_ID field)
Control pointskm_posts.shp (with ROUTE_ID and KNOWN_MEASURE fields)
Control measure fieldKNOWN_MEASURE
Snap tolerance10.0 (meters)
Outputroutes_calibrated.shp

Output adds FROM_MEASURE and TO_MEASURE fields containing the calibrated values.

Recalibrate After Route Edits

If you split, merge, or redraw routes, use recalibration to scale measures proportionally:

Processing Toolbox → Whitebox Workflows → Linear Referencing → Route Recalibrate

ParameterValue
Original routesroutes_calibrated.shp (reference with valid measures)
Edited routesroutes_edited.shp (after geometric changes)
Outputroutes_recalibrated.shp

Step 2 — Validate Route Identifiers

Route IDs must be unique per route and stable across updates. Check for duplicates.

QGIS → Open Attribute Table → Field Calculator or via the Python Console:

from qgis.core import QgsVectorLayer

layer = QgsVectorLayer('/data/routes_m.shp', 'routes', 'ogr')
ids = [f['ROUTE_ID'] for f in layer.getFeatures()]
duplicates = [x for x in ids if ids.count(x) > 1]
if duplicates:
    print(f"Duplicate route IDs found: {set(duplicates)}")
else:
    print("All route IDs are unique.")

Step 3 — Locate Point Events (Dynamic Segmentation)

Processing Toolbox → Whitebox Workflows → Linear Referencing → Locate Point Events

ParameterRecommended value
Input routesroutes_m.shp
Event tableinspections.csv
Route ID field (routes)ROUTE_ID
Route ID field (events)ROUTE_ID
Measure fieldCHAINAGE_M
Outputinspection_points.shp

Each CSV row becomes a point geometry placed at the corresponding measure position on its route. Rows with unmatched route IDs or out-of-range measures are written to an error table.


Step 4 — Locate Line Events (Optional)

If the inspection table records intervals (e.g. pavement condition rated over 100 m segments):

Processing Toolbox → Whitebox Workflows → Linear Referencing → Locate Line Events

ParameterRecommended value
Input routesroutes_m.shp
Event tablepavement.csv
Route ID field (routes)ROUTE_ID
Route ID field (events)ROUTE_ID
From-measure fieldFROM_M
To-measure fieldTO_M
Outputpavement_segments.shp

Step 5 — Inspect and Validate Event Geometry

Load inspection_points.shp in QGIS. Pan to several known inspection records and confirm point positions against the road centreline.

Use QGIS Identify tool to click a point and verify that CHAINAGE_M matches the M-value of the nearest route vertex within acceptable tolerance (typically ± half the route vertex spacing).


Python Console Equivalent

import processing

# Step 3: locate point events
processing.run('whitebox_workflows:locate_point_events', {
    'routes': '/data/routes_m.shp',
    'events': '/data/inspections.csv',
    'route_id_field': 'ROUTE_ID',
    'event_route_id_field': 'ROUTE_ID',
    'measure_field': 'CHAINAGE_M',
    'output': '/data/inspection_points.shp',
})

# Step 4: locate line events
processing.run('whitebox_workflows:locate_line_events', {
    'routes': '/data/routes_m.shp',
    'events': '/data/pavement.csv',
    'route_id_field': 'ROUTE_ID',
    'event_route_id_field': 'ROUTE_ID',
    'from_measure_field': 'FROM_M',
    'to_measure_field': 'TO_M',
    'output': '/data/pavement_segments.shp',
})

print("Linear referencing complete.")

Advanced: Calibrate Routes Against Control Points

If field-collected kilometre posts differ from computed cumulative distance, calibrate M-values by interpolating between control points.

Processing Toolbox → Whitebox Workflows → Linear Referencing → Calibrate Route

ParameterRecommended value
Input routesroutes_m.shp
Calibration pointskm_posts.shp (with ROUTE_ID and KNOWN_M fields)
Route ID fieldROUTE_ID
Measure fieldKNOWN_M
Search tolerance (m)50
Outputroutes_calibrated.shp

After calibration, re-run Locate Point Events on routes_calibrated.shp to position events against field-verified measures.


Common Pitfalls

ProblemLikely causeFix
Events do not locate (0 features output)Route ID field names do not matchCheck both ROUTE_ID parameter values match the actual field names
Events placed far from expected positionM-values in event table use different unitsConfirm both routes and events use the same unit (metres vs km)
Out-of-range events produce no error outputMeasure > route end measureCheck that FROM_M/TO_M do not exceed the route total length
Calibration shifts all events uniformlyOnly one control point per routeAdd at least two control points per route for interpolation
Duplicate route IDs cause incorrect event assignmentMerged route has repeated IDsDissolve route features by ROUTE_ID before building M-values

Validation Checklist

  • Route IDs are unique per route with no duplicates.
  • M-values are monotonically increasing along each route (no reversals).
  • Event table measures fall within the range [0, route total length].
  • Located point events visually snap to the correct road centreline.
  • Line event segment lengths match TO_M - FROM_M within 0.1 m.
  • Error table from Locate Events contains zero unmatched records.

Workflow Index

This index provides task-first entry points for WbW-QGIS workflows. Each entry links to the chapter section where the complete step-by-step example can be found, and lists the key Processing Toolbox tool IDs needed for the task.


Terrain Analysis

TaskChapter sectionKey tools
Compute slope from DEMTerrain Analysis — Step 2whitebox_workflows:slope
Compute aspect from DEMTerrain Analysis — Step 3whitebox_workflows:aspect
Generate hillshade for visualisationTerrain Analysis — Step 4whitebox_workflows:hillshade
Compute profile and plan curvatureTerrain Analysis — Step 5whitebox_workflows:profile_curvature, whitebox_workflows:plan_curvature
Classify terrain into landform elementsTerrain Analysis — Geomorphonswhitebox_workflows:geomorphons
Compute topographic wetness indexTerrain Analysis — Step 6whitebox_workflows:wetness_index
Fill depressions before terrain derivativesTerrain Analysis — Step 1whitebox_workflows:fill_depressions

Spatial Hydrology

TaskChapter sectionKey tools
Condition DEM for hydrologic routingSpatial Hydrology — Step 1whitebox_workflows:breach_depressions_least_cost
Derive D8 flow directionSpatial Hydrology — Step 2whitebox_workflows:d8_pointer
Compute flow accumulationSpatial Hydrology — Step 3whitebox_workflows:d8_flow_accumulation
Extract stream network from accumulationSpatial Hydrology — Step 4whitebox_workflows:extract_streams
Snap pour points to channel rasterSpatial Hydrology — Step 5whitebox_workflows:snap_pour_points
Delineate watershed / catchmentSpatial Hydrology — Step 6whitebox_workflows:watershed
Compute Topographic Wetness IndexSpatial Hydrology — TWIwhitebox_workflows:wetness_index

LiDAR Processing

TaskChapter sectionKey tools
QA — inspect point cloud statisticsLiDAR Processing — Step 1whitebox_workflows:lidar_point_stats
Thin high-density point cloudLiDAR Processing — Step 2whitebox_workflows:lidar_thin
Classify ground returnsLiDAR Processing — Step 3whitebox_workflows:lidar_ground_point_filter
Build DTM from ground-classified cloudLiDAR Processing — Step 4whitebox_workflows:lidar_idw_interpolation
Build DSM from first returnsLiDAR Processing — Step 5whitebox_workflows:lidar_idw_interpolation
Derive canopy height model (CHM)LiDAR Processing — Step 6whitebox_workflows:canopy_height_model
Normalise heights above groundLiDAR Processing — Step 7whitebox_workflows:height_above_ground

Remote Sensing

TaskChapter sectionKey tools
Compute NDVI from multispectral imageRemote Sensing — Step 2whitebox_workflows:ndvi
Threshold vegetation and classify change binsRemote Sensing — Step 3QGIS Raster Calculator, whitebox_workflows:reclass
NDVI/NBR-based change detectionRemote Sensing — Step 3QGIS Raster Calculator
Reduce bands with PCARemote Sensing — Step 4whitebox_workflows:principal_component_analysis
Segment image into homogeneous objectsRemote Sensing — Step 5whitebox_workflows:image_segmentation

Raster Analysis

TaskChapter sectionKey tools
Compute distance from a binary feature rasterRaster Analysis — Step 1whitebox_workflows:euclidean_distance
Reclassify raster into suitability scoresRaster Analysis — Steps 2–4whitebox_workflows:reclass_from_file
Combine reclassified factors by weightRaster Analysis — Step 5QGIS Raster Calculator
Summarise raster values within polygonsRaster Analysis — Step 6QGIS Zonal Statistics
Smooth raster with focal meanRaster Analysis — Focal Statisticswhitebox_workflows:mean_filter

Vector Analysis

TaskChapter sectionKey tools
Validate and repair polygon geometryVector Analysis — Step 1QGIS native:fixgeometries
Add area, perimeter, centroid attributesVector Analysis — Step 2whitebox_workflows:add_geometry_attributes
Join attributes from overlapping polygonsVector Analysis — Step 3whitebox_workflows:spatial_join
Compute distance to nearest featureVector Analysis — Step 4whitebox_workflows:near
Select features by spatial predicateVector Analysis — Step 5QGIS Select by Expression
Simplify polygon boundariesVector Analysis — Simplifywhitebox_workflows:simplify_features
Convert GeoPackage to TopoJSON and backVector Analysis — TopoJSON Conversion Chainwhitebox_workflows:add_geometry_attributes, QGIS Export
Simplify shared boundaries and emit TopoJSONVector Analysis — TopoJSON Boundary-Preserving Generalization Chainwhitebox_workflows:simplify_features, QGIS Export
Convert TopoJSON transport input, enrich, and re-emitVector Analysis — TopoJSON Transport + Enrichment Return ChainQGIS Export, whitebox_workflows:add_geometry_attributes, whitebox_workflows:spatial_join, whitebox_workflows:near

Network Analysis

TaskChapter sectionKey tools
Prepare road network geometry and costsNetwork Analysis — Workflow Awhitebox_workflows:add_geometry_attributes, QGIS geometry tools
Compute shortest path routesNetwork Analysis — Workflow BQGIS Network Analysis shortest path tools
Delineate road service areasNetwork Analysis — Workflow BQGIS native:serviceareafromlayer
Build OD-style batch travel-cost summariesNetwork Analysis — Workflow CQGIS shortest path batch/model workflows
Compute Strahler and Shreve stream hierarchyNetwork Analysis — Workflow Dwhitebox_workflows:strahler_stream_order, whitebox_workflows:shreve_stream_magnitude
Convert raster stream network to vectorNetwork Analysis — Workflow Dwhitebox_workflows:raster_streams_to_vector

Linear Referencing

TaskChapter sectionKey tools
Add measure (M) values to route networkLinear Referencing — Step 1QGIS native:setmvalue
Validate unique route IDsLinear Referencing — Step 2QGIS Python Console check
Locate point events along routesLinear Referencing — Step 3whitebox_workflows:locate_point_events
Locate line events along routesLinear Referencing — Step 4whitebox_workflows:locate_line_events
Calibrate M-values against control pointsLinear Referencing — Calibratewhitebox_workflows:calibrate_route

By Data Type

Raster input tasks

Point cloud input tasks

Vector input tasks

Troubleshooting

Plugin Does Not Appear in QGIS

Checks:

  • Confirm plugin directory path is correct for active QGIS profile.
  • Confirm plugin folder name is whitebox_workflows_qgis.
  • Restart QGIS after install/symlink changes.

Whitebox Provider Missing from Processing

Checks:

  • Confirm plugin is enabled.
  • Trigger discovery refresh.
  • Confirm whitebox_workflows imports in QGIS Python environment.

Tools Are Missing or Unexpectedly Locked

Checks:

  • Rebuild/reinstall whitebox_workflows.
  • Refresh discovery.
  • Confirm runtime capability metadata matches expected tier.
  • Confirm tool taxonomy and generated provider state are synchronized.

Tool Runs but Output Is Missing

Checks:

  • Verify output path exists and is writable.
  • Verify input paths and formats are valid.
  • Re-run on a small dataset to isolate data-specific failures.
  • Check Processing logs for warnings/errors.

Environment Mismatch Problems

Symptoms:

  • import failures in plugin startup,
  • inconsistent behavior between terminal and QGIS,
  • discovery succeeds in one environment but not another.

Resolution:

  • Ensure QGIS and your install command use the same Python environment.
  • Re-run runtime install in that exact environment.