{ "cells": [ { "cell_type": "markdown", "id": "64b14a3e", "metadata": {}, "source": [ "# Scripting wolfhece from a VSCode Jupyter Notebook\n", "\n", "This notebook shows the recommended way to drive a live `WolfMapViewer`\n", "interactively from VSCode — no bridge, no separate server, no extra\n", "dependencies beyond `ipykernel` (already used by VSCode itself).\n", "\n", "## How it works\n", "\n", "IPython has built-in support for wx event-loop integration via the\n", "`%gui wx` magic. When active, IPython drives the wx event loop itself\n", "using a timer-based mechanism — you **must not** call `app.MainLoop()`\n", "because IPython handles it.\n", "\n", "```\n", "%gui wx ← tells IPython to manage the wx event loop\n", " run once, alone in its own cell, before any wx code\n", "```\n", "\n", "Each notebook cell returns immediately; wx windows are live and keep\n", "processing events while you run subsequent cells.\n", "\n", "## Two entry points\n", "\n", "| Class | When to use |\n", "|---|---|\n", "| `WolfMapViewer` | Minimal viewer only — no extra data, fastest start |\n", "| `MapManager` | Full production stack (hydrometry, PICC, cadaster, WMS, …) |\n", "\n", "Sections A and B below cover each option.\n", "\n", "---\n", "> **Requirements** — wolfhece installed in the active kernel's environment.\n", "> The kernel is the same Python that runs in the VSCode terminal." ] }, { "cell_type": "code", "execution_count": 1, "id": "ae503adc", "metadata": {}, "outputs": [], "source": [ "# ── Step 1 (mandatory, run this cell first, alone) ───────────────────────────\n", "# Activate wx event-loop integration. After this cell, wx windows are live\n", "# and the notebook remains interactive simultaneously.\n", "%gui wx" ] }, { "cell_type": "code", "execution_count": null, "id": "5b96bdde", "metadata": {}, "outputs": [], "source": [ "# ── Step 2 ───────────────────────────────────────────────────────────────────\n", "# Create the wx.App singleton. False = do not redirect stdout/stderr.\n", "# Do NOT call app.MainLoop() — %gui wx handles the event loop.\n", "# import wx\n", "# app = wx.App(False)" ] }, { "cell_type": "markdown", "id": "988510b9", "metadata": {}, "source": [ "---\n", "## Section A — Minimal viewer (`WolfMapViewer`)\n", "\n", "Use this when you only need the map canvas, without the full data stack\n", "(no hydrometry GUI, no PICC/cadaster layers, etc.)." ] }, { "cell_type": "code", "execution_count": 3, "id": "4af63ee8", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:Importing wolfhece modules\n", "INFO:root:wolfhece modules imported\n" ] }, { "data": { "text/plain": [ "False" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from wolfhece.PyDraw import WolfMapViewer\n", "\n", "viewer = WolfMapViewer(None, title='wolfhece — notebook session')\n", "viewer.Show()" ] }, { "cell_type": "code", "execution_count": 4, "id": "84357734", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x: [-2.0, 42.0]\n", "y: [-0.9, 40.9]\n" ] } ], "source": [ "# Cells below execute while the viewer is open.\n", "\n", "# Current viewport bounds:\n", "print(f\"x: [{viewer.xmin:.1f}, {viewer.xmax:.1f}]\")\n", "print(f\"y: [{viewer.ymin:.1f}, {viewer.ymax:.1f}]\")" ] }, { "cell_type": "code", "execution_count": 5, "id": "a53b06e9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "File not found: C:\\path\\to\\my_array.bin\n" ] } ], "source": [ "# Load a raster array and display it.\n", "# Adapt the path to your own file.\n", "from pathlib import Path\n", "\n", "array_file = Path(r'C:\\path\\to\\my_array.bin') # ← adapt\n", "\n", "if array_file.exists():\n", " viewer.add_object(which='array', filename=str(array_file), id='DEM', ToCheck=True)\n", " viewer.findminmax(True)\n", " viewer.Autoscale(False)\n", " viewer.Paint()\n", "else:\n", " print(f\"File not found: {array_file}\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "e2b40037", "metadata": {}, "outputs": [], "source": [ "# Direct wx calls are safe — we are on the main thread.\n", "viewer.set_statusbar_text('Hello from the notebook!')\n", "viewer.Paint()" ] }, { "cell_type": "markdown", "id": "4b349eff", "metadata": {}, "source": [ "---\n", "## Section B — Full production stack (`MapManager`)\n", "\n", "`MapManager` wraps a `WolfMapViewer` and automatically loads the standard\n", "data layers: SPW hydrometry stations, PICC parcels, cadaster, land maps,\n", "WMS tiles, coordinate grid, and optional HECE database items.\n", "\n", "### Key points\n", "\n", "- Pass `splash=False` to suppress the splash screen (not needed in a notebook).\n", "- The viewer itself is at `manager.mapviewer`.\n", "- `MapManager` is itself a (hidden) `wx.Frame` — you do not need to call\n", " `manager.Show()`. The viewer frame shows itself." ] }, { "cell_type": "code", "execution_count": 2, "id": "1bab94c5", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:Importing wolfhece modules\n", "INFO:root:wolfhece modules imported\n", "INFO:root:MapManager\n", "INFO:root:MapManager - MapViewer created\n", "INFO:root:MapManager - Data directories created\n", "ERROR:root:Error in hydrometry init: 'numpy.ndarray' object is not callable\n", "INFO:root:MapManager - hydrometry_wolfgui created\n", "INFO:root:MapManager - Picc_data created\n", "INFO:root:MapManager - Cadaster_data created\n", "INFO:root:MapManager - PlansTerrier created\n", "INFO:root:MapManager - Ouvrages Ponts created\n", "INFO:root:MapManager - Ouvrages Seuils created\n", "INFO:root:MapManager - Enquetes created\n", "INFO:root:MapManager - Particularites created\n", "INFO:root:MapManager - PlansTerrier created\n", "INFO:root:MapManager - Menu Landuse/Landcover created\n" ] } ], "source": [ "# Close the minimal viewer from Section A first if it is still open.\n", "# viewer.Close()\n", "\n", "from wolfhece.PyGui import MapManager\n", "\n", "# splash=False → skip the WolfLauncher splash screen\n", "manager = MapManager(splash=False)\n", "\n", "# Convenient shorthand\n", "v = manager.mapviewer" ] }, { "cell_type": "code", "execution_count": 4, "id": "2850074c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Arrays : (none)\n", "Vectors : ['grid']\n" ] } ], "source": [ "# Verify what was loaded automatically.\n", "from wolfhece.PyDraw import draw_type\n", "\n", "arrays = v.get_list_ids(drawing_type=draw_type.ARRAYS, checked_state=None)\n", "vectors = v.get_list_ids(drawing_type=draw_type.VECTORS, checked_state=None)\n", "\n", "print(\"Arrays :\", arrays or '(none)')\n", "print(\"Vectors :\", vectors or '(none)')" ] }, { "cell_type": "code", "execution_count": 5, "id": "7c045ad5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "File not found: C:\\path\\to\\my_array.bin\n" ] } ], "source": [ "# Add a raster, fit the view and repaint.\n", "from pathlib import Path\n", "\n", "array_file = Path(r'C:\\path\\to\\my_array.bin') # ← adapt\n", "\n", "if array_file.exists():\n", " v.add_object(which='array', filename=str(array_file), id='DEM', ToCheck=True)\n", " v.findminmax(True)\n", " v.Autoscale(False)\n", " v.Paint()\n", "else:\n", " print(f\"File not found: {array_file}\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "a797b92d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No active array — click one in the tree.\n" ] } ], "source": [ "# Inspect the active array (selected in the tree on the left).\n", "a = v.active_array\n", "if a is not None:\n", " print(f\"Active array : {a.idx}\")\n", " print(f\" Shape : {a.array.shape}\")\n", " print(f\" Min / Max : {float(a.array.min()):.3f} / {float(a.array.max()):.3f}\")\n", "else:\n", " print('No active array — click one in the tree.')" ] }, { "cell_type": "code", "execution_count": 7, "id": "c01cbb16", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "# Access the SPW hydrometry sub-manager loaded by MapManager.\n", "hydro = manager.SPWhydrometry\n", "print(type(hydro))\n", "# hydro.get_stations() ← example method (check the API)" ] }, { "cell_type": "markdown", "id": "79c17650", "metadata": {}, "source": [ "---\n", "## Section C — Useful scripting patterns\n", "\n", "A collection of common operations once the viewer is live." ] }, { "cell_type": "code", "execution_count": 8, "id": "f68ec11b", "metadata": {}, "outputs": [], "source": [ "# Work with the active array as a NumPy masked array.\n", "import numpy as np\n", "\n", "a = v.active_array\n", "if a is not None:\n", " data = a.array # np.ma.MaskedArray, shape (cols, rows)\n", " valid = data[~data.mask]\n", " print(f\"Valid cells : {valid.size:,}\")\n", " print(f\"Mean : {valid.mean():.3f}\")\n", " print(f\"Std : {valid.std():.3f}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "a30f0918", "metadata": {}, "outputs": [ { "ename": "AttributeError", "evalue": "'WolfMapViewer' object has no attribute 'save_canvas'", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpathlib\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Path\n\u001b[32m 4\u001b[39m out = Path.home() / \u001b[33m'\u001b[39m\u001b[33mwolf_snapshot.png\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m \u001b[43mv\u001b[49m\u001b[43m.\u001b[49m\u001b[43msave_canvas\u001b[49m(\u001b[38;5;28mstr\u001b[39m(out))\n\u001b[32m 6\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mSaved → \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mout\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n", "\u001b[31mAttributeError\u001b[39m: 'WolfMapViewer' object has no attribute 'save_canvas'" ] } ], "source": [ "# Save the current canvas to PNG.\n", "from pathlib import Path\n", "\n", "out = Path.home() / 'wolf_snapshot.png'\n", "v.save_canvasogl(fn=str(out))\n", "print(f'Saved → {out}')" ] }, { "cell_type": "code", "execution_count": null, "id": "f919dc80", "metadata": {}, "outputs": [], "source": [ "# Programmatic zoom to a bounding box (Lambert 72 / 2008 coords).\n", "# v.zoom_on_bounds(xmin, ymin, xmax, ymax)\n", "# v.Autoscale(False)\n", "# v.Paint()" ] }, { "cell_type": "markdown", "id": "93fa8a1f", "metadata": {}, "source": [ "---\n", "## Section D — Comparison with the standalone app (`wolf.py`)\n", "\n", "```python\n", "# wolf.py (standalone)\n", "ex = wx.App()\n", "mywolf = MapManager() # splash=True by default\n", "ex.MainLoop() # blocks until the window closes\n", "```\n", "\n", "```python\n", "# notebook (this file)\n", "%gui wx # IPython manages the event loop\n", "app = wx.App(False)\n", "manager = MapManager(splash=False) # no MainLoop() call\n", "v = manager.mapviewer # keep running cells while the window is live\n", "```\n", "\n", "The only differences are `%gui wx`, `splash=False`, and the absence of\n", "`MainLoop()`. Everything else — objects, methods, data — is identical." ] }, { "cell_type": "markdown", "id": "42641162", "metadata": {}, "source": [ "---\n", "## Section E — Closing\n", "\n", "Close the viewer window normally, or from a cell:" ] }, { "cell_type": "code", "execution_count": null, "id": "bd9def6d", "metadata": {}, "outputs": [], "source": [ "# v.Close() # closes the viewer frame\n", "# manager.Close() # also closes the manager frame (log window, etc.)" ] } ], "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 }