render_lonboard

render_lonboard class

Reactively render a lonboard map.

Usage Source

render_lonboard(
    _fn: Optional[ValueFn[ValueT]] = None,
    *,
    width: Optional[str] = None,
    height: Optional[str] = None,
    fill: Optional[bool] = None,
    fillable: Optional[bool] = None,
    rate_policy: Optional[RatePolicy] = None,
    rate_delay: Optional[float] = None,
)

@render_lonboard is a decorator that creates a render function – a function that returns the map (or its data) to display and re-runs when the reactive values it reads change:

import geopandas as gpd
from shapely.geometry import Point

from shinywidgets import render_lonboard

cities = gpd.GeoDataFrame(
    {"city": ["SF", "NYC"]},
    geometry=[Point(-122.42, 37.77), Point(-74.01, 40.71)],
    crs="EPSG:4326",
)


@render_lonboard
def map():
    return cities

The render function may return a lonboard.Map widget, a lonboard layer (or a list of layers), which is placed on a Map, or any data that lonboard.viz() accepts (e.g., a GeoPandas GeoDataFrame or a GeoArrow-compatible Arrow table), which is converted to a Map via lonboard.viz() (requires pip install 'shinywidgets[lonboard]'). In a filling layout, the map fills its container unless the Map has an explicit height.

Note

Accessing the rendered widget: the render function gains two read-only attributes: .value, the value it most recently returned, and .widget, the rendered Map instance. Map state can also be read reactively with .read("some_trait") (a shortcut for reactive_read() on .widget).

Responding to user interaction: read the map’s accessors in a reactive function (a @render.* function, @reactive.calc, or @reactive.effect), which re-runs whenever the user gestures, much like input.my_slider():

  • map.view_state(): the camera (longitude, latitude, zoom, pitch, bearing).
  • map.selected(): the most recently picked row, across layers.
  • map.selected_rows(): the picked row of a data frame.
  • map.selected_bounds(): the bbox dragged with the Select BBox control.
  • map.clicked(): the most recent map click, as (lng, lat) – note the order; ipyleaflet reports [lat, lng].

They can’t be read inside this output’s own render function, which builds a new map each time it runs. For anything beyond them, use map.read() (a shortcut for reactive_read() on map.widget).

Mutating the widget: use .widget to update the rendered map in place; trait changes on the widget are synced to the browser.

Parameters

width : Optional[str] = None

A CSS length unit (e.g., "400px" or "100%") for the output’s width.

height : Optional[str] = None

A CSS length unit for the output’s height. When None (the default), the output is designed to fill the height of its parent container (see fill and fillable).

fill : Optional[bool] = None

Whether the output should grow or shrink to fit its parent container. When None (the default), this is True if height is not specified.

fillable : Optional[bool] = None

Whether the output container should fill available space (scrolling its contents on overflow). When None (the default), this is True if height is not specified.

rate_policy : Optional[RatePolicy] = None

How to rate-limit view_state() (debounced by default) and reactive_read() on the widget: "debounce" or "throttle". None (the default) keeps the debounce, and debounces reactive_read() only if rate_delay is given.

rate_delay : Optional[float] = None

The rate-limit window in seconds (default 0.25). 0 disables rate limiting.

Each :
which :

See also

output_widget(): in Shiny Core, the UI needs this to hold the output (Shiny Express displays it where the render function is defined).

Methods

clicked() The (lng, lat) of the most recent map click – note the order; ipyleaflet reports [lat, lng] – or None until the first click. Clicking the same spot twice doesn’t re-fire. Kept across re-renders until the user clicks the new map.
selected() The positional index of the most recently picked row, as gdf.iloc[map.selected()], or None until the first pick.
selected_bounds() The bounding box the user dragged with the map’s built-in Select BBox control, as (minx, miny, maxx, maxy) – i.e. (west, south, east, north) in lng/lat – or None until the first selection.
selected_rows() The picked row of df (see selected()), as df.iloc[map.selected()].
view_state() The map’s current view state: a lonboard.view_state.MapViewState (or GlobeViewState) dataclass with longitude, latitude, zoom, pitch, and bearing, in lng/lat order.

clicked()

render_lonboard.clicked()

The (lng, lat) of the most recent map click – note the order; ipyleaflet reports [lat, lng] – or None until the first click. Clicking the same spot twice doesn’t re-fire. Kept across re-renders until the user clicks the new map.

selected()

render_lonboard.selected()

The positional index of the most recently picked row, as gdf.iloc[map.selected()], or None until the first pick.

This accessor attaches at render, and lonboard retains the pick on each layer’s selected_index trait, so a pick made before this accessor’s first read – for example, before a chat handler or LLM tool first reads it – is reflected in that first read instead of being lost. The value follows the displayed map’s retained pick: a re-render keeps it only until the new map reports its own (a freshly built map has no pick, so it becomes None).

With several pickable layers this is the most recent pick across all of them; it doesn’t say which layer, and picking the same index on two different layers in a row doesn’t re-fire – with several pickable layers, observe each layer yourself instead: reactive_read(layer, "selected_index"). When the accessor attaches and more than one layer already holds a pick (possible only for a map that outlives its render, e.g. a shared_lifetime() map), there is no timestamp to say which was most recent, so the seed is None rather than an arbitrary layer’s pick. A layer only reports when pickable=True (the default). Picking the same row of the same layer twice doesn’t re-fire (the trait only changes when the value does), and clicking empty space doesn’t clear it.

The index is a position: if the layer’s data is replaced in place (e.g. layer.table = ... on a long-lived map), a retained index can point at a different row. To track rows across data changes, carry an ID column in the geo data frame and look the pick up by it.

selected_bounds()

render_lonboard.selected_bounds()

The bounding box the user dragged with the map’s built-in Select BBox control, as (minx, miny, maxx, maxy) – i.e. (west, south, east, north) in lng/lat – or None until the first selection.

selected_rows()

render_lonboard.selected_rows(df: Optional[IntoDataFrameT] = None)

The picked row of df (see selected()), as df.iloc[map.selected()].

df defaults to a frame reconstructed from the pickable layer’s own data (the picked rows of layer.table, converted with geopandas.GeoDataFrame.from_arrow), which keeps the layer’s columns but not the original frame’s index (the result gets a fresh RangeIndex); pass df when you need the original frame (or it isn’t a GeoDataFrame). Any frame type works (pandas, polars, …); the result has the same type. Empty while nothing is picked, so it can be passed straight to @render.data_frame.

With several pickable layers the default raises, since a pick doesn’t say which layer it’s from; pass df (the frame of the layer you mean), or read each layer’s pick yourself with reactive_read(layer, "selected_index"), as selected() suggests.

The match is positional, so after a re-render or in-place data replacement (layer.table = ... on a long-lived map), a retained position can point at a different row – carry an ID column and look rows up by it when the data can change. (A replacement does re-run readers; it just can’t tell which of the new rows is “the same”.)

view_state()

render_lonboard.view_state()

The map’s current view state: a lonboard.view_state.MapViewState (or GlobeViewState) dataclass with longitude, latitude, zoom, pitch, and bearing, in lng/lat order.

Available as soon as the map has rendered (it’s computed from the layers), and updated by the browser during each pan or zoom – the browser reports continuously through the gesture, so this accessor is debounced by default (0.25 s; see the renderer’s rate_policy/rate_delay). Never None once rendered; the map is immutable per render, so a re-render starts from the new map’s own view state.