How to Use the GeoLibre Python Package in Jupyter Notebooks with Anywidget
The geolibre package turns the full GeoLibre web app into a Jupyter-compatible widget by leveraging anywidget, starting a localhost static server on import and synchronizing project state via JSON and window.postMessage.
The GeoLibre Python package (geolibre) provides a leafmap-style API that embeds the complete GeoLibre GIS application directly into Jupyter notebooks. Built on top of anywidget, it bridges Python's data science ecosystem with a professional web-based mapping interface. This guide covers installation, core concepts, and practical workflows based on the actual source code implementation in opengeos/GeoLibre.
Installing GeoLibre in Your Jupyter Environment
Install the package from PyPI to get both the Python bindings and the pre-built web assets:
pip install geolibre
The package bundles the complete GeoLibre web build (_STATIC_APP) so no separate frontend compilation is required. Upon first import, a lightweight localhost server automatically starts to serve these assets to the iframe-based widget.
Core Architecture: How GeoLibre Works with Anywidget
The geolibre implementation in python/src/geolibre/geolibre.py centers on a single Map class that extends anywidget.AnyWidget. Understanding these components helps you debug and optimize your notebooks.
The Map Class and Trait Synchronization
The Map class defines traitlet fields marked sync=True to enable automatic two-way communication between Python and the browser:
project— The live project JSON containing layers, styles, and viewport state_app_url,_app_port,_remote_mode— Connection configuration for the embedded server_seq— Sequence counter to force update detection on the frontend
When you modify self.project in Python, the _update_project helper creates a deep copy, increments _seq, and reassigns the trait — triggering a state push to the UI.
Local Static Server and Remote Mode Resolution
On initialization, serve_app(_STATIC_APP) launches a thread-local server (source at lines 77-82). The _resolve_remote_mode method detects your environment:
| Environment | Behavior |
|---|---|
| Local Jupyter | Direct localhost URL to the static server |
| JupyterHub / Binder | Routes through Jupyter-Server-Proxy via the bundled server extension |
| Google Colab | Detected by _running_on_colab() with special handling |
The server extension in backend/geolibre_server/jupyter_server_config.py serves the app under {base_url}geolibre/app/ when server_proxy="auto".
Project Synchronization and RPC
Two communication channels operate simultaneously:
- Trait-based sync — For state changes (layers, viewport, styles)
- Custom message RPC — For method calls requiring responses via
self.send/on_msg(lines 92-96)
This dual-channel design lets you call methods like get_center() or run_algorithm() and receive synchronous-feeling results despite the async widget boundary.
Creating Your First GeoLibre Map in Jupyter
Import and instantiate the Map class with initial viewport settings:
from geolibre import Map
# Create a map centered on the United States
m = Map(center=(-100, 40), zoom=4, basemap="dark", height="700px")
# Display the interactive widget
m
The height parameter controls the iframe size. Valid basemap values include "dark", "light", "satellite", and others supported by the GeoLibre UI.
Adding Data Layers: GeoJSON, COG, and Vector Tiles
GeoJSON Layers
Add remote or local GeoJSON datasets with automatic styling:
m.add_geojson(
"https://raw.githubusercontent.com/opengeos/GeoLibre/main/python/examples/data/sample.geojson",
name="Sample Data"
)
The name parameter becomes the layer identifier for later reference in algorithms and the layer panel.
Cloud-Optimized GeoTIFFs (COG)
Render large raster datasets without local tiling:
m.add_cog(
"https://example.com/dem.tif",
name="Elevation",
colormap="terrain"
)
Vector Tiles
Connect to TileJSON endpoints for dynamic vector rendering:
m.add_vector_tiles(
"https://tiles.example.com/tiles.json",
name="Roads"
)
Changing Basemaps
Switch the background layer programmatically:
m.add_basemap("satellite")
Two-Way Synchronization: Reading and Writing Map State
The project JSON format (.geolibre.json) is identical across desktop, web, and Python builds, enabling portable workflows.
Reading Live UI State
After user interaction in the widget, query the current state:
# Get current map center as [lng, lat]
center = m.get_center()
print("Current center:", center)
# Retrieve complete project configuration
proj = m.to_project()
print("Layer count:", len(proj["layers"]))
print("Zoom level:", proj["view"]["zoom"])
Persisting and Reloading Projects
Save your work and resume later or share with desktop users:
# Save to disk
m.save_project("my_map.geolibre.json")
# Load in a new session
m2 = Map()
m2.load_project("my_map.geolibre.json")
m2 # Renders the restored configuration
Responding to User Events with Callbacks
Register Python functions to handle map interactions:
def on_click(event):
print("Clicked at:", event["lngLat"])
print("Features under cursor:", event.get("features", []))
# Subscribe to events; store the returned function to unsubscribe
unsubscribe = m.on_click(on_click)
# Later: remove the handler
# unsubscribe()
The event dictionary structure matches GeoLibre's internal event format, providing access to coordinates, features, and modifier keys.
Running Geoprocessing Algorithms
GeoLibre exposes its JavaScript-based processing tools to Python via the RPC channel:
# Discover available algorithms
algorithms = m.list_algorithms()
print(algorithms) # e.g., ['buffer', 'dissolve', 'simplify', ...]
# Execute with timeout for long-running operations
result = m.run_algorithm(
"buffer",
{"layer": "Roads", "distance": 1000, "units": "meters"},
timeout=120 # seconds
)
print("Success:", result["success"])
print("Output layer:", result.get("outputLayer"))
print("Logs:", result["logs"])
The timeout parameter is critical for algorithms processing large datasets — the default may need adjustment based on your data volume.
Troubleshooting Common Issues
Widget Not Rendering
- Verify
anywidgetis installed:pip show anywidget - Check browser console for CORS or mixed-content errors
- On JupyterHub, confirm the server extension is enabled:
jupyter serverextension list
Connection Errors in Remote Environments
Force the remote mode explicitly if auto-detection fails:
m = Map(center=(0, 0), zoom=2, _remote_mode="server_proxy")
Project Sync Lag
Large projects with many layers may exhibit update delays. The _seq counter in the source code ensures eventual consistency, but consider m.to_project() calls after batch operations to force synchronization.
Summary
- Installation:
pip install geolibre— bundles complete web build, no compilation needed - Architecture:
Mapclass extendsanywidget.AnyWidgetwith trait-based sync and custom RPC messages inpython/src/geolibre/geolibre.py - Server: Auto-starts localhost static server; auto-detects JupyterHub/Colab/Binder via
_resolve_remote_mode - Data support: GeoJSON, COG rasters, vector tiles (TileJSON), multiple basemaps
- State management: Portable
.geolibre.jsonformat;save_project/load_projectfor persistence - Interactivity: Event callbacks, two-way sync, and algorithm execution with timeout control
Frequently Asked Questions
Does GeoLibre work in Google Colab?
Yes. The Map class detects Colab environments via _running_on_colab() and adjusts its communication strategy accordingly. Import and use geolibre exactly as in local Jupyter — the remote-mode resolution handles the underlying complexity automatically.
How is GeoLibre different from leafmap or ipyleaflet?
GeoLibre embeds a full-featured GIS application rather than assembling map components. While leafmap provides Pythonic wrappers around Leaflet, GeoLibre's Map class exposes a complete desktop-grade interface with built-in layer management, styling tools, and processing algorithms — all synchronized through the anywidget bridge.
Can I use GeoLibre without the automatic server?
Partially. The Map class requires the server for iframe content. However, the project JSON format is compatible with standalone GeoLibre builds, so you can save_project() and open files in the desktop or web versions without Python.
Why are my Python changes not appearing in the map immediately?
Check that you're modifying the project through methods, not direct trait assignment. The _update_project helper in the source code manages deep copies and sequence bumps. Use methods like add_geojson() rather than direct m.project["layers"].append(...) to ensure proper sync triggering.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →