manywidgets.lonboard ships control widgets for lonboard
maps. They’re an optional subpackage:
pip install "manywidgets[lonboard]"These are control-plane widgets — they reference a lonboard Map/layer and mutate
its traits; they don’t render the map. They work in a live kernel and in static
export (no kernel), resolving the layer through @manywidgets/core’s resolveModel
and fanning each write out to every proxy of the layer (the Map keeps its own).
Widgets¶
LayerToggle— show/hide a layer (visible).FilterBinder— drive a layer’sfilter_rangefrom aRangeSlider(DataFilterExtension).LayerFilter— filter a layer by category (filter_categories) via a checkbox legend.MapFlyer— buttons that animate theMapto preset locations (fly_to).
Compose them with the layout widgets to put a control panel beside the map:
from manywidgets import Column
from manywidgets.lonboard import LayerToggle, FilterBinder
Column(LayerToggle(layer), FilterBinder(slider, layer), m)Recipe: a layer switcher¶
A Map can hold several layers; one LayerToggle per layer (in a Column) is a
layer switcher. Works for any layer type — vector or raster (BitmapTileLayer):
from lonboard import Map, BitmapTileLayer
from manywidgets import Column
from manywidgets.lonboard import LayerToggle
osm = BitmapTileLayer(data="https://tile.openstreetmap.org/{z}/{x}/{y}.png", visible=True)
topo = BitmapTileLayer(data="https://a.tile.opentopomap.org/{z}/{x}/{y}.png", visible=False)
m = Map([osm, topo], basemap=None)
Column(
Column(LayerToggle(osm, label="OpenStreetMap"), LayerToggle(topo, label="OpenTopoMap")),
m,
)See it live in the interop example.
Recipe: data-driven styling + legend¶
Colour a layer by a data attribute with a per-row get_fill_color array, and pair
it with a Legend built from the same palette:
import numpy as np
from manywidgets import Legend
palette = np.array([[230, 30, 30], [30, 160, 30], [30, 90, 230]], dtype="uint8")
layer = ScatterplotLayer.from_geopandas(gdf, get_fill_color=palette[categories]) # one colour per row
legend = Legend([[palette[i].tolist(), name] for i, name in enumerate(["A", "B", "C"])], title="Category")(For binned continuous data, bin the values and label the ranges, e.g.
["0–10", "10–20", …].) See the
interop example.
Recipe: fly to preset locations¶
MapFlyer repositions an already-rendered Map — something Map.view_state can’t do
(it’s uncontrolled: deck.gl reads it once as initialViewState). Each preset is a
dict with a label and camera keys; clicking a button animates the map there:
from lonboard import Map
from manywidgets import Column
from manywidgets.lonboard import MapFlyer
m = Map(layer)
flyer = MapFlyer(m, locations=[
{"label": "New York", "longitude": -74.0, "latitude": 40.7, "zoom": 10},
{"label": "London", "longitude": -0.12, "latitude": 51.5, "zoom": 9},
], duration=3000)
Column(flyer, m)It drives lonboard’s existing fly_to (a deck.gl FlyToInterpolator animation) from the
browser — no kernel needed — so it works the same live and in static export.
Caveats¶
MapFlyeris fire-and-forget. A fly-to is a one-shot animation command, not stored state, so it positions the map on click, not on load, and won’t replay if the map re-renders. Set the starting position via theMap’s ownview_state.Seconds, not milliseconds for time filters —
DataFilterExtensioncompares as float32 in the shader, so millisecond timestamps overflow its exact-integer range. Use seconds forget_filter_valueand the slider bounds.Static export needs a pre-execute. lonboard layers carry binary Arrow buffers; execute the notebook (
jupyter nbconvert --execute) so the buffers embed in the page.Basemap. By default the
Mapuses lonboard’s carto basemap, whose tiles are fetched from the network when the page is viewed (fine for an online docs site). Passbasemap=Nonefor a fully-offline page (blank background; the data layer still renders).
See the interop example for a working map.