Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

lonboard interop

A small lonboard map driven by manywidgets.lonboard controls — a LayerToggle, a FilterBinder (RangeSliderfilter_range), and a LayerFilter (category checkboxes) — laid out beside the map with Column. Works live and in static export (no kernel); the carto basemap tiles load from the network when you view the page.

import geopandas as gpd
import numpy as np
from shapely.geometry import Point
from lonboard import Map, ScatterplotLayer, BitmapTileLayer
from lonboard.layer_extension import DataFilterExtension

from manywidgets import RangeSlider, Column, Legend
from manywidgets.lonboard import LayerToggle, FilterBinder, LayerFilter
n = 6
vals = np.arange(n, dtype="float64")
cats = np.array([0, 1, 2, 0, 1, 2], dtype="uint8")
pts = [Point(-122.42 + 0.01 * i, 37.77 + 0.01 * i) for i in range(n)]
gdf = gpd.GeoDataFrame(geometry=pts, crs="EPSG:4326")  # accessors passed as arrays below

layer = ScatterplotLayer.from_geopandas(
    gdf,
    get_radius=300,
    radius_units="meters",
    get_fill_color=[200, 30, 30],
    extensions=[DataFilterExtension(filter_size=1, category_size=1)],
    get_filter_value=vals,
    filter_range=(0, 5),
    get_filter_category=cats,
    filter_categories=[0, 1, 2],
)
# Default carto basemap (tiles fetched from the network at view time). Pass
# basemap=None for a fully-offline page.
m = Map(layer, view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 11})

Controls beside the map

Toggle visibility, filter by value range, and filter by category.

toggle = LayerToggle(layer, label="Points", value=True)
rng = RangeSlider(label="Value range", min=0, max=5, low=0, high=5, step=1)
fb = FilterBinder(rng, layer)  # rng.low/high -> layer.filter_range
cat_filter = LayerFilter(layer, categories=[[0, "A"], [1, "B"], [2, "C"]], label="Category")

Column(toggle, rng, fb, cat_filter, m, gap="10px")

Data-driven styling + legend

Colour the points by a data attribute (here, category) with a per-row get_fill_color array, and show a matching Legend built from the same palette so the map and legend stay consistent.

palette = np.array([[230, 30, 30], [30, 160, 30], [30, 90, 230]], dtype="uint8")
labels = ["A", "B", "C"]

styled = ScatterplotLayer.from_geopandas(
    gdf,
    get_radius=400,
    radius_units="meters",
    get_fill_color=palette[cats],  # data-driven: one colour per row, by category
)
styled_map = Map(styled, view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 11})
legend = Legend(
    [[palette[i].tolist(), labels[i]] for i in range(len(labels))],
    title="Category",
)

Column(legend, styled_map, gap="10px")

Raster layer switcher

A Map can hold several raster (BitmapTileLayer) layers; a LayerToggle per layer makes a layer switcher — just stack the toggles in a Column. Each toggle writes its layer’s visible, so this works live and in static export (the raster tiles load from the network when viewed).

osm = BitmapTileLayer(
    data="https://tile.openstreetmap.org/{z}/{x}/{y}.png",
    tile_size=256, max_zoom=19, visible=True,
)
topo = BitmapTileLayer(
    data="https://a.tile.opentopomap.org/{z}/{x}/{y}.png",
    tile_size=256, max_zoom=17, opacity=0.7, visible=False,
)
raster_map = Map(
    [osm, topo],
    basemap=None,  # the raster layers are the imagery
    view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 10},
)

switcher = Column(
    LayerToggle(osm, label="OpenStreetMap", value=True),
    LayerToggle(topo, label="OpenTopoMap", value=False),
    gap="4px",
)
Column(switcher, raster_map, gap="10px")

Fly to presets

MapFlyer repositions an already-rendered map from the browser — no kernel, so it works in this statically-exported page. Click a button to fly.

from manywidgets.lonboard import MapFlyer

fly_layer = ScatterplotLayer.from_geopandas(
    gdf, get_radius=300, radius_units="meters", get_fill_color=[30, 90, 230]
)
fly_map = Map(fly_layer, view_state={"longitude": -122.4, "latitude": 37.8, "zoom": 11})
flyer = MapFlyer(fly_map, locations=[
    {"label": "San Francisco", "longitude": -122.42, "latitude": 37.77, "zoom": 12},
    {"label": "New York", "longitude": -74.0, "latitude": 40.70, "zoom": 11},
    {"label": "London", "longitude": -0.12, "latitude": 51.50, "zoom": 10},
], duration=3000)
Column(flyer, fly_map, gap="10px")