Skip to content

Notebook panel

GeoLibre can run a Jupyter notebook beside the map, in a resizable, collapsible panel (open it from Processing → Jupyter Notebook). It works in both the web and desktop builds, with a different Python runtime behind the same <iframe> panel:

  • Web build → JupyterLite. A full Jupyter UI running entirely in the browser on a Pyodide (WebAssembly) kernel — no server. Install extra packages on demand from a cell with %pip install geopandas.
  • Desktop build → a real JupyterLab server. The app launches jupyter lab from a uv-managed Python environment (the same mechanism as the FastAPI sidecar) on 127.0.0.1:8766, token-authenticated, and embeds it. This gives full CPython with the native geospatial stack (geopandas, rasterio, GDAL, …).

This is distinct from the geolibre Python package, which does the inverse — embedding the whole GeoLibre app inside a notebook cell.

Scripting the map from a cell

A lightweight geolibre client is preloaded into the notebook kernel — on web it is bundled into JupyterLite's filesystem, so no pip install is needed; on desktop it is placed on the kernel's import path. Just import it:

import geolibre

m = geolibre.connect()          # or geolibre.Map()
m.fly_to(-122.4, 37.8, zoom=11) # animate the live map in the left pane
m.add_geojson(gdf, name="My layer")   # GeoDataFrame, dict, or JSON string
m.fit_bounds([-123, 37, -122, 38])
m.set_basemap("https://…/style.json")
m.set_visibility(layer_id, False)
m.remove_layer(layer_id)

Calls are fire-and-forget: each posts a command to the host app over the shared scripting protocol (the same createScriptingHandlers surface used by the in-app Python console and the Jupyter widget) and returns immediately, so the client behaves identically on the in-browser kernel and a real server. The app-side endpoint is useNotebookBridge in apps/geolibre-desktop/src/hooks/. Canonical client source: backend/geolibre_server/notebook_client.py.

Read-back queries (e.g. get_center) are not exposed by this fire-and-forget client; they need the blocking request/reply path the geolibre widget uses.

Theme

On the web build the notebook follows the app's light/dark theme live (no reload). JupyterLite is built with exposeAppInBrowser (see apps/geolibre-desktop/jupyterlite/jupyter-lite.json), which puts its app object on window.jupyterapp; since the JupyterLite iframe is same-origin, useNotebookThemeSync reaches in and runs the built-in apputils:change-theme command whenever the app theme changes. On the desktop build the JupyterLab server is a different origin, so this cross-origin call is a no-op and the notebook keeps its own theme (syncing it there would need a small JupyterLab extension — a future enhancement).

Building the web JupyterLite site

The web build embeds a self-hosted JupyterLite site under apps/geolibre-desktop/public/jupyterlite/. It is not committed (≈70 MB) and is generated by a best-effort prebuild step that needs the jupyter lite CLI:

pip install -r apps/geolibre-desktop/jupyterlite/requirements.txt
npm run build:jupyterlite

Both npm run dev and npm run build run this automatically:

  • npm run dev builds it once on first run via a predev hook (--if-missing), then is instant on subsequent runs.
  • npm run build always rebuilds it (via prebuild) so a changed client/config is picked up.

Both skip gracefully when jupyter lite is not installed — a Node-only build still succeeds and the web Notebook panel shows a "not built" message until the site is generated (install the deps above and re-run). The desktop (Tauri) dev and build paths skip it entirely (they use the real JupyterLab server), so the static site never bloats the installer.

Build config lives in apps/geolibre-desktop/jupyterlite/ (a jupyter_lite_config.json, the build requirements.txt, and a starter files/Welcome.ipynb). The generated directory is excluded from the PWA precache (see pwaPlugin in apps/geolibre-desktop/vite.config.ts).

Desktop server

  • Backend extra: notebook in backend/geolibre_server/pyproject.toml (jupyterlab, jupyter-server), synced into its own uv project environment so it never disturbs the sidecar's env.
  • Launcher / lifecycle: start_jupyter_server / stop_jupyter_server Tauri commands in apps/geolibre-desktop/src-tauri/src/lib.rs (mirrors the sidecar launcher), with the TS wrapper in apps/geolibre-desktop/src/lib/jupyter.ts.
  • Framing: backend/geolibre_server/jupyter_server_config.py relaxes Content-Security-Policy: frame-ancestors to the Tauri webview / loopback origins so the app can embed the server; the Tauri CSP (tauri.conf.json) adds the loopback origins to frame-src/child-src.