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 resolution |
|
Factory (origin+nb+d) |
|
Factory (bounds+d) |
|
Factory (slices+d) |
|
Get bounding box |
|
Coords ↔ indices |
|
Batch conversion |
|
Compare headers |
|
File I/O |
|