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:
- Read raster.
- Inspect metadata.
- Transform values (tool-driven or array-driven).
- 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()andraster_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. withscope exit flushes any pinned writes safely.- For pure row-wise transforms,
get_row_data/set_row_datais 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().nodatawhen 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
| Method | Description |
|---|---|
from_numpy | Create a raster from a NumPy array while inheriting grid geometry from a base raster. |
band | Return a band-specific raster view for multiband data. |
to_numpy | Export the active band or all bands to NumPy for custom numeric work. |
deep_copy | Write a full raster copy to a derived or explicit output path. |
new_from_other, new_from_other_with_data | Create a new raster that inherits geometry from another raster, optionally with a new data buffer. |
metadata | Return the RasterConfigs summary for rows, columns, resolution, bounds, and nodata. |
calculate_clip_values | Calculate percentile-based lower and upper clip values. |
calculate_mean, calculate_mean_and_stdev | Compute simple raster summary statistics. |
normalize | Produce a normalized raster suitable for display or downstream scaling steps. |
reinitialize_values | Reset all cells to a single constant value. |
update_min_max | Recompute cached minimum and maximum values after edits. |
num_cells, num_valid_cells | Report total cell count and valid-cell count. |
size_of, get_data_size_in_bytes | Report approximate in-memory or backing-data size. |
is_memory_backed | Indicate whether the raster is currently memory-backed. |
get_short_filename, get_file_extension | Return convenience filename information for reporting or output naming. |
Grid Navigation and Direct Editing
| Method | Description |
|---|---|
get_value, set_value | Read or write an individual cell value. |
pin | Return a pinned raster view for low-overhead random read/write access in a with scope. |
get_row_data, set_row_data | Read or replace an entire row of raster values. |
increment, decrement | Add to or subtract from a single cell value. |
increment_row_data, decrement_row_data | Add to or subtract from every value in a row. |
is_cell_nodata | Check whether a specific cell is nodata. |
get_row_from_y, get_y_from_row | Convert between world Y coordinates and raster row indices. |
get_column_from_x, get_x_from_column | Convert between world X coordinates and raster column indices. |
CRS and Reprojection
| Method | Description |
|---|---|
crs_wkt, crs_epsg | Inspect the raster CRS as WKT text or inferred/declared EPSG code. |
set_crs_wkt, set_crs_epsg | Assign CRS metadata without moving the raster grid. |
clear_crs | Remove CRS metadata when it is known to be wrong or must be re-assigned. |
reproject | Reproject the raster with explicit control over resampling, extent, resolution, and grid policies. |
reproject_nearest, reproject_bilinear | Reproject with a fixed resampling method for common nearest-neighbour or bilinear cases. |
reproject_to_match_grid | Reproject onto the exact grid geometry of a target raster. |
reproject_to_match_resolution | Reproject while matching the cell resolution of a reference raster. |
reproject_to_match_resolution_in_epsg | Reproject 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.
| Method | Description |
|---|---|
abs | Absolute value transform. |
acos, arccos | Inverse cosine transform. |
acosh | Inverse hyperbolic cosine transform. |
asin, arcsin | Inverse sine transform. |
asinh | Inverse hyperbolic sine transform. |
atan, arctan | Inverse tangent transform. |
atanh | Inverse hyperbolic tangent transform. |
cbrt | Cube-root transform. |
ceil, floor, round, trunc | Standard rounding-family transforms. |
clamp | Limit values to a minimum and maximum range. |
cos, cosh, sin, sinh, tan, tanh | Trigonometric and hyperbolic transforms. |
degrees, to_degrees, radians, to_radians | Convert angular units between radians and degrees. |
exp, exp2, expm1 | Exponential transforms. |
ln, log10, log1p, log2 | Natural-log and common logarithmic transforms. |
neg, signum, sqrt, square, recip | Negation, sign extraction, square root, squaring, and reciprocal transforms. |
is_finite, is_infinite, is_nan, is_nodata | Build masks from numeric validity tests. |
logical_not, logical_not_in_place, not_ | Logical-not style mask inversion. |
Binary Arithmetic and Comparisons
| Method | Description |
|---|---|
add, add_in_place | Add another raster or scalar, either to a new raster or in place. |
sub, subtract, sub_in_place | Subtract another raster or scalar. |
mul, multiply, mul_in_place | Multiply by another raster or scalar. |
div, divide, div_in_place | Divide by another raster or scalar. |
pow, power, pow_in_place | Raise values to a raster/scalar power. |
atan2 | Compute two-argument arctangent from paired raster/scalar inputs. |
min, max | Compute cellwise minima or maxima. |
eq, eq_in_place | Equality comparison. |
ne, ne_in_place | Inequality comparison. |
gt, gt_in_place | Greater-than comparison. |
ge, ge_in_place | Greater-than-or-equal comparison. |
lt, lt_in_place | Less-than comparison. |
le, le_in_place | Less-than-or-equal comparison. |
Logical Combination
| Method | Description |
|---|---|
and_ | Bitwise-style cellwise AND combination. |
or_ | Bitwise-style cellwise OR combination. |
xor_ | Bitwise-style cellwise XOR combination. |
logical_and, logical_and_in_place | Logical AND for boolean or mask rasters. |
logical_or, logical_or_in_place | Logical OR for boolean or mask rasters. |
logical_xor, logical_xor_in_place | Logical XOR for boolean or mask rasters. |