Working with Rasters

This chapter documents practical raster workflows in WbW-Py with emphasis on inspection, iteration, modification, and persistence.

Raster workflows usually alternate between high-performance tool operations and targeted custom logic. The important concept is to choose the lowest-cost path for each step: use backend tools for heavy transformations, and reserve NumPy-level iteration for domain-specific adjustments that tools do not expose directly.

Raster Lifecycle

This lifecycle helps you separate inspection from transformation so assumptions about CRS, resolution, and nodata are explicit before heavy operations.

Typical lifecycle:

  1. Read raster.
  2. Inspect metadata.
  3. Transform values (tool-driven or array-driven).
  4. Persist outputs with explicit options when needed.
import whitebox_workflows as wb

wbe = wb.WbEnvironment()
dem = wbe.read_raster('dem.tif')
meta = dem.metadata()

print(meta.rows, meta.columns)
print('EPSG:', meta.epsg_code)
print('NoData:', meta.nodata)

Memory-Backed Rasters for Pipeline Efficiency

For workflows that chain multiple tool operations, memory-backed rasters eliminate disk I/O between steps. This is especially valuable when processing large rasters in complex pipelines. Rasters remain in process memory, accessible to subsequent tools without writing intermediate results to disk.

Load a raster into memory with file_mode="m":

import whitebox_workflows as wb

wbe = wb.WbEnvironment()

# Read directly into memory; no disk I/O for subsequent operations
dem = wbe.read_raster('dem.tif', file_mode='m')
slope = wbe.read_raster('slope.tif', file_mode='m')

# Both rasters are now memory-backed. Chain operations without disk:
result = dem.add(slope)
print(result.file_path)  # prints: memory://raster/...

Memory-backed paths are compatible with all downstream raster operations:

import whitebox_workflows as wb

wbe = wb.WbEnvironment()
dem = wbe.read_raster('dem.tif', file_mode='m')

# Inspect metadata
meta = dem.metadata()
print(f"Rows: {meta.rows}, Cols: {meta.columns}")

# Chain tool operations
scaled = wbe.run_tool('multiply', {
    'input': dem.file_path,
    'multiplier': 1.5
})

# Export to disk when ready
wbe.write_raster(scaled, 'dem_scaled_1p5x.tif')

Memory Lifecycle and Cleanup

Memory-backed rasters persist in the process store until explicitly removed or cleared. For long-running jobs, manage memory explicitly to avoid accumulation:

import whitebox_workflows as wb

wbe = wb.WbEnvironment()

# Check current memory usage
count_before = wbe.raster_memory_count()
bytes_before = wbe.raster_memory_bytes()
print(f"Rasters in memory: {count_before}")
print(f"Bytes used: {bytes_before}")

# Read two rasters
dem1 = wbe.read_raster('large_dem1.tif', file_mode='m')
dem2 = wbe.read_raster('large_dem2.tif', file_mode='m')

print(f"After reads: {wbe.raster_memory_count()}")

# Remove one raster when done
wbe.remove_raster_from_memory(dem1)
print(f"After remove: {wbe.raster_memory_count()}")

# Or clear all rasters at once
wbe.clear_raster_memory()
print(f"After clear: {wbe.raster_memory_count()}")

Best practices:

  • Use file_mode='m' for intermediate results in tool chains.
  • Export memory-backed rasters to disk with write_raster() when persisting results.
  • Call remove_raster_from_memory() after a raster is no longer needed.
  • Use clear_raster_memory() between independent job phases.
  • Use clear_memory() when resetting all in-process raster/vector/lidar stores together.
  • Monitor raster_memory_count() and raster_memory_bytes() in large pipelines.

Iterating Through Grid Cells

Use this pattern only when tool methods or vectorized operations cannot express your custom rule directly.

For cell-level logic, convert to NumPy and iterate safely.

import numpy as np
import whitebox_workflows as wb

wbe = wb.WbEnvironment()
r = wbe.read_raster('dem.tif')
a = r.to_numpy(all_bands=False, dtype='float64')
meta = r.metadata()

rows, cols = a.shape
for row in range(rows):
    for col in range(cols):
        z = a[row, col]
        if np.isfinite(z) and z != meta.nodata:
            # Example transform: clamp negatives.
            if z < 0:
                a[row, col] = 0.0

Fast Random Cell Access with Pinning

For custom neighbourhood logic, flow-path traversal, or other pseudo-random cell access, use pinned raster views. Pinning avoids repeated per-access lookup and lock-routing overhead by holding a direct in-memory view during the loop.

Single-raster pattern:

import whitebox_workflows as wb

wbe = wb.WbEnvironment()
pointer = wbe.read_raster("D8Pointer.tif")

with pointer.pin() as p:
    v = p[100, 200]
    p[100, 200] = v + 1.0

Multi-raster scan-loop pattern (read one raster, write another):

import whitebox_workflows as wb

wbe = wb.WbEnvironment()
src = wbe.read_raster("D8Pointer.tif")
dst = wb.Raster.new_from_other(src, data_type="int32")

with wb.pin_rasters(src, dst) as (srcp, dstp):
    meta = src.metadata()
    for row in range(meta.rows):
        for col in range(meta.columns):
            value = srcp[row, col]
            # Replace this with your custom per-cell rule.
            dstp[row, col] = value

Notes:

  • Use pinning for loops dominated by scalar r[row, col] accesses.
  • with scope exit flushes any pinned writes safely.
  • For pure row-wise transforms, get_row_data/set_row_data is still efficient.

Writing Modified Data Back

This example shows the common pattern of deriving a new raster while preserving georeferencing context from a base raster.

import whitebox_workflows as wb

wbe = wb.WbEnvironment()
base = wbe.read_raster('dem.tif')
a = base.to_numpy(all_bands=False)
a = a * 1.05

out = wb.Raster.from_numpy(a, base, output_path='dem_scaled.tif')
wbe.write_raster(out, 'dem_scaled_cog.tif', options={
    'compress': True,
    'strict_format_options': True,
    'geotiff': {'layout': 'cog', 'tile_size': 512},
})

Supported raster write keys and valid values are documented in Output Controls.

Multi-Band Iteration

Use this structure when per-band logic differs or when your transform depends on band-specific rules.

import numpy as np
import whitebox_workflows as wb

wbe = wb.WbEnvironment()
rgb = wbe.read_raster('multiband.tif')
arr = rgb.to_numpy(all_bands=True, dtype='float32')
# arr shape is typically (bands, rows, cols)

bands, rows, cols = arr.shape
for b in range(bands):
    for row in range(rows):
        for col in range(cols):
            v = arr[b, row, col]
            if np.isfinite(v):
                arr[b, row, col] = max(v, 0.0)

wb.Raster.from_numpy(arr, rgb, output_path='multiband_clamped.tif')

Tool-First Raster Processing

This is the preferred pattern for scale: run optimized tools first, then apply targeted custom fixes only where necessary.

Use tools for most heavy computation, then iterate only where custom logic is needed.

import whitebox_workflows as wb

wbe = wb.WbEnvironment()
dem = wbe.read_raster('dem.tif')
filled = wbe.hydrology.depressions_storage.fill_depressions(dem)
slope = wbe.terrain.derivatives.slope(filled)

# Optional post-processing pass in NumPy.
a = slope.to_numpy(all_bands=False)
a[a < 0] = 0
slope_fixed = wb.Raster.from_numpy(a, slope)

wbe.write_raster(slope_fixed, 'slope_fixed.tif')

NoData and Performance Guidance

  • Always check metadata().nodata when doing per-cell iteration.
  • Prefer vectorized NumPy operations over nested Python loops when possible.
  • Use tool methods (wbe.hydrology.*, wbe.raster.*, wbe.terrain.*) for large transforms.
  • Persist only final outputs where possible to keep memory-first workflows efficient.

Raster Object Method Reference

The tables below focus on callable Raster methods. Common simple properties such as file_path, file_name, active_band, and band_count are omitted from the tables to keep the reference readable.

Construction, Conversion, and Summary

MethodDescription
from_numpyCreate a raster from a NumPy array while inheriting grid geometry from a base raster.
bandReturn a band-specific raster view for multiband data.
to_numpyExport the active band or all bands to NumPy for custom numeric work.
deep_copyWrite a full raster copy to a derived or explicit output path.
new_from_other, new_from_other_with_dataCreate a new raster that inherits geometry from another raster, optionally with a new data buffer.
metadataReturn the RasterConfigs summary for rows, columns, resolution, bounds, and nodata.
calculate_clip_valuesCalculate percentile-based lower and upper clip values.
calculate_mean, calculate_mean_and_stdevCompute simple raster summary statistics.
normalizeProduce a normalized raster suitable for display or downstream scaling steps.
reinitialize_valuesReset all cells to a single constant value.
update_min_maxRecompute cached minimum and maximum values after edits.
num_cells, num_valid_cellsReport total cell count and valid-cell count.
size_of, get_data_size_in_bytesReport approximate in-memory or backing-data size.
is_memory_backedIndicate whether the raster is currently memory-backed.
get_short_filename, get_file_extensionReturn convenience filename information for reporting or output naming.

Grid Navigation and Direct Editing

MethodDescription
get_value, set_valueRead or write an individual cell value.
pinReturn a pinned raster view for low-overhead random read/write access in a with scope.
get_row_data, set_row_dataRead or replace an entire row of raster values.
increment, decrementAdd to or subtract from a single cell value.
increment_row_data, decrement_row_dataAdd to or subtract from every value in a row.
is_cell_nodataCheck whether a specific cell is nodata.
get_row_from_y, get_y_from_rowConvert between world Y coordinates and raster row indices.
get_column_from_x, get_x_from_columnConvert between world X coordinates and raster column indices.

CRS and Reprojection

MethodDescription
crs_wkt, crs_epsgInspect the raster CRS as WKT text or inferred/declared EPSG code.
set_crs_wkt, set_crs_epsgAssign CRS metadata without moving the raster grid.
clear_crsRemove CRS metadata when it is known to be wrong or must be re-assigned.
reprojectReproject the raster with explicit control over resampling, extent, resolution, and grid policies.
reproject_nearest, reproject_bilinearReproject with a fixed resampling method for common nearest-neighbour or bilinear cases.
reproject_to_match_gridReproject onto the exact grid geometry of a target raster.
reproject_to_match_resolutionReproject while matching the cell resolution of a reference raster.
reproject_to_match_resolution_in_epsgReproject to a target EPSG while borrowing cell resolution from a reference raster.

Unary Math and Numeric Transforms

Unary transform calls return a single Raster object. Band selection (band_mode='all'|'active'|'list' and bands=[...]) controls which bands are changed within that returned raster.

MethodDescription
absAbsolute value transform.
acos, arccosInverse cosine transform.
acoshInverse hyperbolic cosine transform.
asin, arcsinInverse sine transform.
asinhInverse hyperbolic sine transform.
atan, arctanInverse tangent transform.
atanhInverse hyperbolic tangent transform.
cbrtCube-root transform.
ceil, floor, round, truncStandard rounding-family transforms.
clampLimit values to a minimum and maximum range.
cos, cosh, sin, sinh, tan, tanhTrigonometric and hyperbolic transforms.
degrees, to_degrees, radians, to_radiansConvert angular units between radians and degrees.
exp, exp2, expm1Exponential transforms.
ln, log10, log1p, log2Natural-log and common logarithmic transforms.
neg, signum, sqrt, square, recipNegation, sign extraction, square root, squaring, and reciprocal transforms.
is_finite, is_infinite, is_nan, is_nodataBuild masks from numeric validity tests.
logical_not, logical_not_in_place, not_Logical-not style mask inversion.

Binary Arithmetic and Comparisons

MethodDescription
add, add_in_placeAdd another raster or scalar, either to a new raster or in place.
sub, subtract, sub_in_placeSubtract another raster or scalar.
mul, multiply, mul_in_placeMultiply by another raster or scalar.
div, divide, div_in_placeDivide by another raster or scalar.
pow, power, pow_in_placeRaise values to a raster/scalar power.
atan2Compute two-argument arctangent from paired raster/scalar inputs.
min, maxCompute cellwise minima or maxima.
eq, eq_in_placeEquality comparison.
ne, ne_in_placeInequality comparison.
gt, gt_in_placeGreater-than comparison.
ge, ge_in_placeGreater-than-or-equal comparison.
lt, lt_in_placeLess-than comparison.
le, le_in_placeLess-than-or-equal comparison.

Logical Combination

MethodDescription
and_Bitwise-style cellwise AND combination.
or_Bitwise-style cellwise OR combination.
xor_Bitwise-style cellwise XOR combination.
logical_and, logical_and_in_placeLogical AND for boolean or mask rasters.
logical_or, logical_or_in_placeLogical OR for boolean or mask rasters.
logical_xor, logical_xor_in_placeLogical XOR for boolean or mask rasters.