Spatial header for WOLF arrays

The header_wolf class describes the spatial layout of a WOLF array: origin, resolution, shape, translation, null value, EPSG code, and multi-block support.

Every WolfArray (or WolfArrayModel) owns a header_wolf; understanding it is essential before working with any grid data.

[1]:
from wolfhece.wolf_array import header_wolf, WolfArray
from wolfhece.wolf_array._header_wolf import (
    WOLF_ARRAY_FULL_SINGLE,
    WOLF_ARRAY_FULL_DOUBLE,
    WOLF_ARRAY_FULL_INTEGER,
    WOLF_ARRAY_FULL_INTEGER8,
)

Creating a header from scratch

[2]:
h = header_wolf()

# Set spatial properties
h.set_origin(220000.0, 130000.0)  # Lambert 72 coordinates
h.set_resolution(1.0, 1.0)        # 1 m x 1 m cells
h.nbx = 100                        # 100 columns
h.nby = 80                         # 80 rows
h.nullvalue = -99999.0

print(f"Origin     : {h.origx}, {h.origy}")
print(f"Resolution : {h.dx} x {h.dy}")
print(f"Shape      : {h.nbx} x {h.nby}")
print(f"Null value : {h.nullvalue}")
Origin     : 220000.0, 130000.0
Resolution : 1.0 x 1.0
Shape      : 100 x 80
Null value : -99999.0

Factory methods — make, make_from_xybounds_grid, from_slices

Instead of setting each attribute one by one, several factory methods create a header_wolf in a single call.

[13]:
# --- make: origin + cell count + resolution ---
h_make = header_wolf().make(
    orig=(220000.0, 130000.0),
    nb=(100, 80),
    d=(1.0, 1.0),
)
print("make():")
print(f"  Origin : ({h_make.origx}, {h_make.origy})")
print(f"  Shape  : {h_make.nbx} x {h_make.nby}")
print(f"  Res    : {h_make.dx} x {h_make.dy}")

# --- make_from_xybounds_grid: from spatial bounds ---
h_bounds = header_wolf.make_from_xybounds_grid(
    xbounds=(220000.0, 220100.0),
    ybounds=(130000.0, 130080.0),
    d=(2.0, 2.0),  # 2 m resolution
)
print("\nmake_from_xybounds_grid():")
print(f"  Origin : ({h_bounds.origx}, {h_bounds.origy})")
print(f"  Shape  : {h_bounds.nbx} x {h_bounds.nby}")
print(f"  Res    : {h_bounds.dx} x {h_bounds.dy}")

# --- make_from_xybounds_grid with grid alignment ---
# Snap origin to an existing grid so cells are aligned
h_aligned = header_wolf.make_from_xybounds_grid(
    xbounds=(220003.7, 220100.0),
    ybounds=(130002.1, 130080.0),
    d=(1.0, 1.0),
    grid_to_align=h,  # align to 'h' (origin at integer coords)
)
print("\nmake_from_xybounds_grid(grid_to_align=h):")
print(f"  Origin : ({h_aligned.origx}, {h_aligned.origy})  (snapped)")

# --- from_slices: from Python slice objects ---
h_sliced = header_wolf.from_slices(
    x_slice=slice(10, 60),  # columns 10..59
    y_slice=slice(5, 45),   # rows 5..44
    d=(1.0, 1.0),
)
print("\nfrom_slices():")
print(f"  Origin : ({h_sliced.origx}, {h_sliced.origy})")
print(f"  Shape  : {h_sliced.nbx} x {h_sliced.nby}")
make():
  Origin : (220000.0, 130000.0)
  Shape  : 100 x 80
  Res    : 1.0 x 1.0

make_from_xybounds_grid():
  Origin : (220000.0, 130000.0)
  Shape  : 50 x 40
  Res    : 2.0 x 2.0

make_from_xybounds_grid(grid_to_align=h):
  Origin : (220003.0, 130002.0)  (snapped)

from_slices():
  Origin : (10.0, 5.0)
  Shape  : 50 x 40

Spatial extent and bounds

[3]:
# Bounding box [xmin, xmax, ymin, ymax]
extent = h.get_extent()
print(f"Extent : {extent}")

# Bounds as ([xmin,xmax], [ymin,ymax])
bounds = h.get_bounds()
print(f"Bounds : {bounds}")
Extent : [220000.0, 220100.0, 130000.0, 130080.0]
Bounds : ([220000.0, 220100.0], [130000.0, 130080.0])

Coordinate ↔ Index conversions

Convert between real-world coordinates (x, y) and array indices (i, j).

[4]:
import numpy as np

# Single point: (x, y) -> (i, j)
x, y = 220050.5, 130040.5
i, j = h.get_ij_from_xy(x, y)
print(f"({x}, {y}) -> (i={i}, j={j})")

# Back to coordinates: (i, j) -> (x, y)
x2, y2 = h.get_xy_from_ij(i, j)
print(f"(i={i}, j={j}) -> ({x2}, {y2})")

# Batch conversion with numpy arrays
xy = np.array([[220010.0, 130010.0],
               [220050.0, 130050.0],
               [220090.0, 130070.0]])
ij = h.get_ij_from_xy_array(xy)
print(f"\nBatch xy->ij:\n{ij}")
(220050.5, 130040.5) -> (i=50, j=40)
(i=50, j=40) -> (220050.5, 130040.5)

Batch xy->ij:
[[10 10]
 [50 50]
 [90 70]]

Comparing and combining headers

[7]:
# Create a second header (overlapping region)
h2 = header_wolf()
h2.set_origin(220050.0, 130030.0)
h2.set_resolution(1.0, 1.0)
h2.nbx = 80
h2.nby = 60

# Check if headers are compatible
print(f"Same layout? {h.is_like(h2)}")

# Find intersection -> ([xmin, xmax], [ymin, ymax]) or None
inter = h.find_intersection(h2)
if inter is not None:
    (xmin, xmax), (ymin, ymax) = inter
    print(f"Intersection extent: x=[{xmin}, {xmax}], y=[{ymin}, {ymax}]")

    print(f"Intersection size  : {xmax - xmin:.0f} x {ymax - ymin:.0f} m")

# Find union
union = h.find_union(h2)
(uxmin, uxmax), (uymin, uymax) = union
print(f"Union extent       : x=[{uxmin}, {uxmax}], y=[{uymin}, {uymax}]")
Same layout? False
Intersection extent: x=[220050.0, 220100.0], y=[130030.0, 130080.0]
Intersection size  : 50 x 50 m
Union extent       : x=[220000.0, 220130.0], y=[130000.0, 130090.0]

Reading and writing header files

WOLF headers are stored as .txt sidecar files alongside array data.

[11]:
from pathlib import Path
import tempfile

tmpdir = Path(tempfile.mkdtemp())

# Write header to file (wolftype = data type constant)
h.write_txt_header(str(tmpdir / 'demo.txt'), wolftype=WOLF_ARRAY_FULL_SINGLE)

# Read it back
h_read = header_wolf()
h_read.read_txt_header(str(tmpdir / 'demo'))
print(f"Read back: origin=({h_read.origx}, {h_read.origy}), shape={h_read.nbx}x{h_read.nby}")
Read back: origin=(220000.0, 130000.0), shape=100x80

Extracting a header from a WolfArray

Every WolfArray exposes its header via get_header().

[12]:
# Create a WolfArray and inspect its header
wa = WolfArray(srcheader=h, whichtype=WOLF_ARRAY_FULL_SINGLE)

h_from_array = wa.get_header()
print(f"Origin : ({h_from_array.origx}, {h_from_array.origy})")
print(f"dtype  : {wa.array.dtype}")
Origin : (220000.0, 130000.0)
dtype  : float32

Summary

Task

Method

Set origin

set_origin(x, y)

Set resolution

set_resolution(dx, dy)

Factory (origin+nb+d)

make(orig, nb, d)

Factory (bounds+d)

make_from_xybounds_grid(xbounds, ybounds, d, grid_to_align)

Factory (slices+d)

from_slices(x_slice, y_slice, d)

Get bounding box

get_extent(), get_bounds()

Coords ↔ indices

get_ij_from_xy(), get_xy_from_ij()

Batch conversion

get_ij_from_xy_array(), get_xy_from_ij_array()

Compare headers

is_like(), find_intersection(), find_union()

File I/O

read_txt_header(), write_txt_header()