{ "cells": [ { "cell_type": "markdown", "id": "6812b7ac", "metadata": {}, "source": [ "# Spatial header for WOLF arrays\n", "\n", "The `header_wolf` class describes the spatial layout of a WOLF array:\n", "origin, resolution, shape, translation, null value, EPSG code, and multi-block support.\n", "\n", "Every `WolfArray` (or `WolfArrayModel`) owns a `header_wolf`; understanding it is essential\n", "before working with any grid data." ] }, { "cell_type": "code", "execution_count": 1, "id": "bbd58ef5", "metadata": {}, "outputs": [], "source": [ "from wolfhece.wolf_array import header_wolf, WolfArray\n", "from wolfhece.wolf_array._header_wolf import (\n", " WOLF_ARRAY_FULL_SINGLE,\n", " WOLF_ARRAY_FULL_DOUBLE,\n", " WOLF_ARRAY_FULL_INTEGER,\n", " WOLF_ARRAY_FULL_INTEGER8,\n", ")" ] }, { "cell_type": "markdown", "id": "f33e5584", "metadata": {}, "source": [ "## Creating a header from scratch" ] }, { "cell_type": "code", "execution_count": 2, "id": "9be00b01", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Origin : 220000.0, 130000.0\n", "Resolution : 1.0 x 1.0\n", "Shape : 100 x 80\n", "Null value : -99999.0\n" ] } ], "source": [ "h = header_wolf()\n", "\n", "# Set spatial properties\n", "h.set_origin(220000.0, 130000.0) # Lambert 72 coordinates\n", "h.set_resolution(1.0, 1.0) # 1 m x 1 m cells\n", "h.nbx = 100 # 100 columns\n", "h.nby = 80 # 80 rows\n", "h.nullvalue = -99999.0\n", "\n", "print(f\"Origin : {h.origx}, {h.origy}\")\n", "print(f\"Resolution : {h.dx} x {h.dy}\")\n", "print(f\"Shape : {h.nbx} x {h.nby}\")\n", "print(f\"Null value : {h.nullvalue}\")" ] }, { "cell_type": "markdown", "id": "00c32730", "metadata": {}, "source": [ "## Factory methods — `make`, `make_from_xybounds_grid`, `from_slices`\n", "\n", "Instead of setting each attribute one by one, several factory methods create a\n", "`header_wolf` in a single call." ] }, { "cell_type": "code", "execution_count": 13, "id": "c671b0b0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "make():\n", " Origin : (220000.0, 130000.0)\n", " Shape : 100 x 80\n", " Res : 1.0 x 1.0\n", "\n", "make_from_xybounds_grid():\n", " Origin : (220000.0, 130000.0)\n", " Shape : 50 x 40\n", " Res : 2.0 x 2.0\n", "\n", "make_from_xybounds_grid(grid_to_align=h):\n", " Origin : (220003.0, 130002.0) (snapped)\n", "\n", "from_slices():\n", " Origin : (10.0, 5.0)\n", " Shape : 50 x 40\n" ] } ], "source": [ "# --- make: origin + cell count + resolution ---\n", "h_make = header_wolf().make(\n", " orig=(220000.0, 130000.0),\n", " nb=(100, 80),\n", " d=(1.0, 1.0),\n", ")\n", "print(\"make():\")\n", "print(f\" Origin : ({h_make.origx}, {h_make.origy})\")\n", "print(f\" Shape : {h_make.nbx} x {h_make.nby}\")\n", "print(f\" Res : {h_make.dx} x {h_make.dy}\")\n", "\n", "# --- make_from_xybounds_grid: from spatial bounds ---\n", "h_bounds = header_wolf.make_from_xybounds_grid(\n", " xbounds=(220000.0, 220100.0),\n", " ybounds=(130000.0, 130080.0),\n", " d=(2.0, 2.0), # 2 m resolution\n", ")\n", "print(\"\\nmake_from_xybounds_grid():\")\n", "print(f\" Origin : ({h_bounds.origx}, {h_bounds.origy})\")\n", "print(f\" Shape : {h_bounds.nbx} x {h_bounds.nby}\")\n", "print(f\" Res : {h_bounds.dx} x {h_bounds.dy}\")\n", "\n", "# --- make_from_xybounds_grid with grid alignment ---\n", "# Snap origin to an existing grid so cells are aligned\n", "h_aligned = header_wolf.make_from_xybounds_grid(\n", " xbounds=(220003.7, 220100.0),\n", " ybounds=(130002.1, 130080.0),\n", " d=(1.0, 1.0),\n", " grid_to_align=h, # align to 'h' (origin at integer coords)\n", ")\n", "print(\"\\nmake_from_xybounds_grid(grid_to_align=h):\")\n", "print(f\" Origin : ({h_aligned.origx}, {h_aligned.origy}) (snapped)\")\n", "\n", "# --- from_slices: from Python slice objects ---\n", "h_sliced = header_wolf.from_slices(\n", " x_slice=slice(10, 60), # columns 10..59\n", " y_slice=slice(5, 45), # rows 5..44\n", " d=(1.0, 1.0),\n", ")\n", "print(\"\\nfrom_slices():\")\n", "print(f\" Origin : ({h_sliced.origx}, {h_sliced.origy})\")\n", "print(f\" Shape : {h_sliced.nbx} x {h_sliced.nby}\")" ] }, { "cell_type": "markdown", "id": "d18266e3", "metadata": {}, "source": [ "## Spatial extent and bounds" ] }, { "cell_type": "code", "execution_count": 3, "id": "6ede2ee6", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Extent : [220000.0, 220100.0, 130000.0, 130080.0]\n", "Bounds : ([220000.0, 220100.0], [130000.0, 130080.0])\n" ] } ], "source": [ "# Bounding box [xmin, xmax, ymin, ymax]\n", "extent = h.get_extent()\n", "print(f\"Extent : {extent}\")\n", "\n", "# Bounds as ([xmin,xmax], [ymin,ymax])\n", "bounds = h.get_bounds()\n", "print(f\"Bounds : {bounds}\")" ] }, { "cell_type": "markdown", "id": "9441dc89", "metadata": {}, "source": [ "## Coordinate ↔ Index conversions\n", "\n", "Convert between real-world coordinates `(x, y)` and array indices `(i, j)`." ] }, { "cell_type": "code", "execution_count": 4, "id": "ee6f809e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(220050.5, 130040.5) -> (i=50, j=40)\n", "(i=50, j=40) -> (220050.5, 130040.5)\n", "\n", "Batch xy->ij:\n", "[[10 10]\n", " [50 50]\n", " [90 70]]\n" ] } ], "source": [ "import numpy as np\n", "\n", "# Single point: (x, y) -> (i, j)\n", "x, y = 220050.5, 130040.5\n", "i, j = h.get_ij_from_xy(x, y)\n", "print(f\"({x}, {y}) -> (i={i}, j={j})\")\n", "\n", "# Back to coordinates: (i, j) -> (x, y)\n", "x2, y2 = h.get_xy_from_ij(i, j)\n", "print(f\"(i={i}, j={j}) -> ({x2}, {y2})\")\n", "\n", "# Batch conversion with numpy arrays\n", "xy = np.array([[220010.0, 130010.0],\n", " [220050.0, 130050.0],\n", " [220090.0, 130070.0]])\n", "ij = h.get_ij_from_xy_array(xy)\n", "print(f\"\\nBatch xy->ij:\\n{ij}\")" ] }, { "cell_type": "markdown", "id": "b15ad6d3", "metadata": {}, "source": [ "## Comparing and combining headers" ] }, { "cell_type": "code", "execution_count": 7, "id": "b6388939", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Same layout? False\n", "Intersection extent: x=[220050.0, 220100.0], y=[130030.0, 130080.0]\n", "Intersection size : 50 x 50 m\n", "Union extent : x=[220000.0, 220130.0], y=[130000.0, 130090.0]\n" ] } ], "source": [ "# Create a second header (overlapping region)\n", "h2 = header_wolf()\n", "h2.set_origin(220050.0, 130030.0)\n", "h2.set_resolution(1.0, 1.0)\n", "h2.nbx = 80\n", "h2.nby = 60\n", "\n", "# Check if headers are compatible\n", "print(f\"Same layout? {h.is_like(h2)}\")\n", "\n", "# Find intersection -> ([xmin, xmax], [ymin, ymax]) or None\n", "inter = h.find_intersection(h2)\n", "if inter is not None:\n", " (xmin, xmax), (ymin, ymax) = inter\n", " print(f\"Intersection extent: x=[{xmin}, {xmax}], y=[{ymin}, {ymax}]\")\n", "\n", " print(f\"Intersection size : {xmax - xmin:.0f} x {ymax - ymin:.0f} m\")\n", "\n", "# Find union\n", "union = h.find_union(h2)\n", "(uxmin, uxmax), (uymin, uymax) = union\n", "print(f\"Union extent : x=[{uxmin}, {uxmax}], y=[{uymin}, {uymax}]\")" ] }, { "cell_type": "markdown", "id": "2b30665f", "metadata": {}, "source": [ "## Reading and writing header files\n", "\n", "WOLF headers are stored as `.txt` sidecar files alongside array data." ] }, { "cell_type": "code", "execution_count": 11, "id": "4151104c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Read back: origin=(220000.0, 130000.0), shape=100x80\n" ] } ], "source": [ "from pathlib import Path\n", "import tempfile\n", "\n", "tmpdir = Path(tempfile.mkdtemp())\n", "\n", "# Write header to file (wolftype = data type constant)\n", "h.write_txt_header(str(tmpdir / 'demo.txt'), wolftype=WOLF_ARRAY_FULL_SINGLE)\n", "\n", "# Read it back\n", "h_read = header_wolf()\n", "h_read.read_txt_header(str(tmpdir / 'demo'))\n", "print(f\"Read back: origin=({h_read.origx}, {h_read.origy}), shape={h_read.nbx}x{h_read.nby}\")" ] }, { "cell_type": "markdown", "id": "574ac76c", "metadata": {}, "source": [ "## Extracting a header from a WolfArray\n", "\n", "Every `WolfArray` exposes its header via `get_header()`." ] }, { "cell_type": "code", "execution_count": 12, "id": "3c46f23e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Origin : (220000.0, 130000.0)\n", "dtype : float32\n" ] } ], "source": [ "# Create a WolfArray and inspect its header\n", "wa = WolfArray(srcheader=h, whichtype=WOLF_ARRAY_FULL_SINGLE)\n", "\n", "h_from_array = wa.get_header()\n", "print(f\"Origin : ({h_from_array.origx}, {h_from_array.origy})\")\n", "print(f\"dtype : {wa.array.dtype}\")" ] }, { "cell_type": "markdown", "id": "3ff6dfaf", "metadata": {}, "source": [ "## Summary\n", "\n", "| Task | Method |\n", "|------|--------|\n", "| Set origin | `set_origin(x, y)` |\n", "| Set resolution | `set_resolution(dx, dy)` |\n", "| Factory (origin+nb+d) | `make(orig, nb, d)` |\n", "| Factory (bounds+d) | `make_from_xybounds_grid(xbounds, ybounds, d, grid_to_align)` |\n", "| Factory (slices+d) | `from_slices(x_slice, y_slice, d)` |\n", "| Get bounding box | `get_extent()`, `get_bounds()` |\n", "| Coords ↔ indices | `get_ij_from_xy()`, `get_xy_from_ij()` |\n", "| Batch conversion | `get_ij_from_xy_array()`, `get_xy_from_ij_array()` |\n", "| Compare headers | `is_like()`, `find_intersection()`, `find_union()` |\n", "| File I/O | `read_txt_header()`, `write_txt_header()` |" ] } ], "metadata": { "kernelspec": { "display_name": "python311", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }