Skip to main content

shiny_deckgl

Shiny for Python → deck.gl bridge (Java-free)

A lightweight library for integrating deck.gl (v9.4.0) and MapLibre GL JS (v6.11.2) into Shiny for Python applications. Built for marine science and GIS visualisation — WMS layers, EMODnet, HELCOM, food web modelling — the package handles CDN asset injection, layer construction, and bi-directional messaging between the Python server and the browser, all without Java dependencies.

Features

Core (v0.1)

Capability Details
Reusable MapWidget Drop-in widget with configurable initial view state, auto-render on connect, and update() to push new layers at any time.
Layer helpers scatterplot_layer(), geojson_layer(), tile_layer(), bitmap_layer() — typed factory functions with sensible defaults and **kwargs pass-through.
Generic layer() Any deck.gl layer class via layer("HeatmapLayer", ...); the JS client resolves deck[type] dynamically.
WMS / TileLayer First-class WMS support via tile_layer() — render EMODnet, GEBCO, or any {bbox-epsg-3857} service.
Click & hover events Read click/hover results in Python via input[widget.click_input_id]() / input[widget.hover_input_id]().
Tooltips Configure tooltip={"html": "<b>{name}</b>"} on MapWidget for automatic hover tooltips with template interpolation.
Viewport read-back input[widget.view_state_input_id]() gives lon/lat/zoom/pitch/bearing after every camera move.
Basemap styles CARTO_POSITRON, CARTO_DARK, CARTO_VOYAGER, OSM_LIBERTY — or any MapLibre/Mapbox style URL.
Mapbox API key MapWidget(..., mapbox_api_key="pk.xxx") to use mapbox:// style URLs with automatic token injection.
Color scales color_range(), color_bins(), color_quantiles() with 5 built-in palettes.
Layer visibility set_layer_visibility(session, {"layer1": False}) toggles layers without resending data.
Draggable markers add_drag_marker(session) places a marker the user can reposition.
DataFrame support Pass pandas DataFrames or GeoDataFrames directly as layer data — auto-converted to records / GeoJSON.
Extensions layer(..., extensions=["DataFilterExtension"]) — resolved on the client.
Binary transport encode_binary_attribute(np_array) — base64-encodes numpy arrays for high-performance typed-array attributes.
Lighting & effects Pass effects=[{"type": "LightingEffect", ...}] to update() for ambient/point/directional lighting.
Multiple views map_view(), orthographic_view(), first_person_view() helpers for multi-view layouts.
HTML export widget.to_html(layers, path="map.html") — standalone HTML file viewable in any browser.
JSON spec to_json() / from_json() for serialising and restoring map configurations.
@@ accessor convention Python strings like "@@d" or "@@d.position" are resolved to JS arrow functions on the client.
CDN-pinned assets deck.gl 9.4.0, MapLibre GL 6.11.2 — deterministic builds. Standalone to_html() exports pin MapLibre 5.24.0 (see below).
Conda recipe Bundled conda.recipe/meta.yaml for micromamba / conda-build.

Phase 1 — Controls & Navigation (v0.2)

Capability Details
MapLibre v5 upgrade Upgraded from MapLibre GL JS v3.6.2 to v5.24.0 (breaking API changes handled).
Map controls add_control(session, "NavigationControl") — navigation, scale, fullscreen, geolocate, attribution, terrain.
remove_control() Remove previously added controls by type.
fit_bounds() Fly the camera to a bounding box with optional padding and animation.
Map click events input[widget.map_click_input_id]() — clicks on the basemap (not on deck.gl layers).

Phase 2 — Native Sources & Layers (v0.3)

Capability Details
add_source() Add native MapLibre sources (GeoJSON, vector, raster, raster-dem, image, video).
remove_source() Remove a previously added source by id.
add_maplibre_layer() Add native MapLibre style layers (fill, line, circle, symbol, heatmap, fill-extrusion, raster, hillshade).
remove_maplibre_layer() Remove a native layer by id.
set_paint_property() / set_layout_property() Mutate paint or layout properties on existing native layers.
set_filter() Set a layer filter expression.
set_style() Change the entire basemap style dynamically (warns that native layers will be lost).
add_image() / remove_image() Load custom icons (PNG/SVG/WebP) for use with symbol layers; SDF recolouring supported.

Phase 3 — Globe, Terrain & Popups (v0.4)

Capability Details
Globe projection set_projection(session, "globe") — switch between mercator and globe.
Terrain set_terrain(session, source, exaggeration) — 3D terrain with DEM source.
Popups add_popup() / remove_popup() — HTML popups on native MapLibre layers.
Spatial queries query_rendered_features(), query_at_lnglat() — query visible features.
Multiple markers add_marker(), remove_marker(), clear_markers() — programmatic markers with optional popups and drag support.

Phase 4 — Drawing, GeoPandas & Export (v0.5)

Capability Details
Drawing tools enable_draw() / disable_draw() — MapboxDraw integration with point/line/polygon modes.
Get/delete drawn features get_drawn_features(), delete_drawn_features() — manage drawn geometry.
GeoPandas integration add_geodataframe() — directly add a GeoDataFrame as a GeoJSON source + native layers.
Feature state set_feature_state() / remove_feature_state() — interactive styling without redrawing.
Map export export_image(format="png"/"jpeg"/"webp") — screenshot the map as a base64 data URL.

Phase 5 — Control Helpers & Legend Plugins (v0.6)

Capability Details
set_controls() Replace all MapLibre controls at once with a list of control specs.
Control helpers geolocate_control(), globe_control(), terrain_control() — typed factory functions.
Legend control legend_control() — native MapLibre layer legend with checkboxes (@watergis/maplibre-gl-legend).
Opacity control opacity_control() — layer switcher with opacity sliders (maplibre-gl-opacity).
deck.gl legend deck_legend_control() — custom legend panel for deck.gl overlay layers with 5 swatch shapes.

Phase 6 — Extended Layer Helpers (v0.7)

Capability Details
10 new layer helpers arc_layer(), icon_layer(), path_layer(), line_layer(), text_layer(), column_layer(), polygon_layer(), heatmap_layer(), hexagon_layer(), h3_hexagon_layer().
Deck-level properties parameters, effects, views — full control over the deck.gl Deck instance through update().
Layer extensions layer(..., extensions=["DataFilterExtension"]) with auto-resolution on the client.

Phase 7 — Widgets, Camera Transitions & Globe (v0.8)

Capability Details
deck.gl widgets set_widgets() with 21 helpers: zoom_widget(), compass_widget(), fullscreen_widget(), scale_widget(), gimbal_widget(), reset_view_widget(), screenshot_widget(), loading_widget(), timeline_widget(), geocoder_widget(), theme_widget(), popup_widget(), icon_widget(), toggle_widget(), selector_widget(), scrollbar_widget(), layer_legend_widget() + 4 experimental; widget state changes arrive as input[widget.widget_event_input_id].
Camera transitions fly_to(longitude, latitude, zoom, speed) — smooth MapLibre flyTo animation.
ease_to(longitude, latitude, zoom, duration) — linear easeTo animation.
Transition helper transition(duration, easing, type) — animate layer property changes on data update.
Globe view globe_view() — render the earth as a 3D globe with GlobeView.
deck.gl 9.3.6 CDN upgraded from 9.1.4 to 9.3.6 with widget support.

Phase 8 — Animation, Geo-Layers & Drawing Demo (v0.9)

Capability Details
TripsLayer trips_layer() for animated vessel/vehicle tracks with _tripsAnimation config.
GreatCircleLayer great_circle_layer() for geodesic arcs.
ContourLayer contour_layer() for isoline/isoband visualisations.
GridLayer grid_layer() for rectangular spatial binning with 3-D extrusion.
ScreenGridLayer screen_grid_layer() for screen-space density grids.
MVTLayer mvt_layer() for Mapbox Vector Tiles.
WMSLayer wms_layer() for deck.gl 9.x native WMS layers.
Interleaved rendering MapWidget(interleaved=True) for deck/MapLibre layer interleaving.
Client-side animation requestAnimationFrame-based TripsLayer animation loop — no server polling.
Drawing demo Tab 7 — MapboxDraw tools, named markers with popups, spatial query, live interaction log.
Animation demo Tab 8 — Animated Baltic shipping tracks, GreatCircleLayer, GridLayer, speed/trail controls.

v1.12.0 — MapLibre 6.11, MapLibreOverlay, Widget Events & Reconnect

Capability Details
MapLibre 6.11.2 Served apps get 6.11.2 (worker-tile leak, setStyle terrain crash and Map#once fixes); exports stay on 5.24.0.
MapLibreOverlay The deck.gl integration is now the one deck.gl recommends for MapLibre. before_id= on any layer orders it under a basemap layer in interleaved mode.
Widget events input[widget.widget_event_input_id] reports timeline, toggle, selector, icon, popup, stats, theme, geocoder, zoom, fullscreen, loading and reset-view changes as {"id", "widget", "event", "value"}.
New widgets popup_widget(), icon_widget(), toggle_widget(), selector_widget(), scrollbar_widget(); zoom_widget(zoom_step=). fps_widget() / view_selector_widget() are deprecated.
Map options MapWidget(map_options={...}) passes maxBounds, maxPitch, renderWorldCopies, antialias, ... to MapLibre.
Reconnect With session.allow_reconnect() (Shiny 1.8), input[widget.reconnected_input_id] fires and resend_last_update(session) restores the map.
3-D picking pickable="3d" reaches deck.gl; the click/hover coordinate gains a z value.
Headless demo test tests/test_demo_headless.py runs two demo tabs under shiny.testserver.

v1.11.1 — Follow-ups

Capability Details
Per-session controller set_controller() is stored per session, so exports match the live map's interaction settings.
Seal IBM demo Superseded simulation runs are cancelled; no stale result flashes before the new one.
Colour helpers Integers too large for a float count as missing instead of raising.
Tests The parser tests no longer all skip when pyproj is missing.

v1.11.0 — Security, Legends, Data Safety & One Animation Loop

Capability Details
Accessor sandbox closed @@= expressions could reach new Function() through f?.(...) plus a string-built key. Optional calls are now rejected and computed keys must be literals or d.a.b paths.
Sanitiser hardening SVG animation elements and <math> are stripped; output is re-sanitised until stable.
Layer legend widget Unticked layers stay listed; include_hidden=True; toggles reported via legend_visibility_input_id; collapse state survives updates; real colour ramps for aggregation layers.
MapLibre legend (changed) legend_control() without targets lists the app's native layers, not the basemap's; targets={} restores the old behaviour. reverse_order defaults to True.
Native layers & styles Layers added after load are no longer stranded; set_style(diff=False) is honoured.
Data safety numpy/pandas/date values no longer crash the session; projected GeoDataFrames are reprojected to EPSG:4326; SHYFEM parsers take crs=; colour helpers handle NaN and Series.
Per-session state (changed) update_tooltip() / set_cooperative_gestures() no longer mutate the shared widget; use current_tooltip(session).
Exports to_html() / to_json() keep controls, controller and interaction settings.
Performance Layers are resolved once and reused; one animation loop per map; animation frames no longer repaint the basemap.
Removed The unused _mixins package.

v1.10.1 — data: URI Hardening & CI Repair

Capability Details
data: URIs restricted Sanitised tooltip/popup HTML now allows data: only for inert raster images (png, jpe?g, gif, webp, bmp, avif, x-icon). data:text/html executes its markup when navigated to, and data:image/svg+xml runs its own <script> and event handlers — both are rejected. Layer config such as ICON_ATLAS never passes through the sanitiser and is unaffected.
CI restored hypothesis, pytest-benchmark and pillow were used by the suite but declared nowhere, so pytest aborted during collection on every push. All three are now declared.
test extra Runs the suite without the geospatial stack. pandas is listed explicitly — it previously arrived only as a geopandas dependency, so dropping geopandas alone silently skipped every pandas-guarded test.
Branch coverage in CI python-package also runs on release/** and fix/**, so a branch is tested before it can reach main.
Benchmarks execute tests/test_benchmarks.py had never run on any machine — pytest-benchmark was absent everywhere. Its 36 tests now run and pass.

v1.10.0 — Review Fixes, MapLibre 6 & deck.gl 9.4

Capability Details
MapLibre GL JS 6.7.0 Served apps load v6 (ESM, dynamic import()). Standalone to_html() exports deliberately stay on 5.24.0 — v6's module worker dies on file://, leaving the basemap silently unloaded.
deck.gl 9.4.0 Upgraded from 9.3.6, together with @deck.gl/widgets.
CoordinateSystem is a string enum (breaking) deck.gl 9 identifies coordinate systems by name; the enum still carried deck.gl 8's integers, so the default scatterplot_layer() silently failed to render. Python-side comparisons such as cs == 1 no longer hold.
Python ≥ 3.10 (breaking) Shiny has required 3.10 since 1.6.2.
Maps initialised on the first tab The client waited on a shiny:connected event that never fired.
Widget helpers restored deck.gl promoted several widgets out of experimental; six of eighteen helpers resolved to nothing. fps_widget and view_selector_widget remain unavailable — deck.gl ships no such class in any 9.x release.
Non-ASCII text labels TextLayer used the default ASCII atlas, so "Klaipėda" rendered a gap. characterSet="auto" builds the atlas from the supplied labels.
javascript: filter hardened The guard was a substring regex, so java<TAB>script: survived it — browsers strip TAB/LF/CR before parsing the scheme.
normalize_rows() An all-zero row is an absorbing state and normalises to a self-loop (P[k, k] = 1); it previously produced a row that rng.choice rejects. Non-square matrices still fall back to uniform.
Demo console is clean The always-built Google 3D Tiles layer answered 403 without an API key on every page load; it is now built only while its toggle is on.

v1.9.2 — Robustness & Correctness Fixes

Capability Details
partial_update() safety No longer mutates the caller's layer dicts in-place — uses shallow copies so original DataFrames survive repeated calls.
to_html() reliability Standalone HTML export now checks isStyleLoaded() before pushing layers, preventing silent layer loss when the style is already cached.
Style-swap robustness whenStyleReady queues all deferred callbacks (not just the first); timeout and handler cleanup prevents stale listeners on rapid set_style() calls.
TripsLayer resume Pause/resume correctly preserves playback position instead of silently restarting from time 0.
SVG atlas in partial updates deck_partial_update now pre-rasterises SVG icon atlases before building layers (matches deck_update).
Binary decode performance decodeBinaryValue uses Uint8Array.from() — eliminates per-byte string copies for large binary attributes.
Mapbox API key safety transformRequest restricted to mapbox:// and api.mapbox.com URLs — no longer leaks tokens to third-party tiles containing "mapbox" in the path.
Zero-value view state All parseFloat || default patterns replaced with isNaN guards — zoom=0, longitude=0, latitude=0 no longer silently coerced.
color_bins() formula Corrected equal-width binning from n_bins-1 to n_bins scale factor.
Antimeridian handling in_bounds() now correctly filters points when the viewport straddles the 180°/-180° line.
Viewport debounce Generation check before AND after fn() prevents wasted computation on rapid pan/zoom.
.grd parser hardening UTF-8 encoding with replace fallback; bounds checks prevent IndexError on malformed lines.
Thread-safe demo data random.seed() replaced with random.Random() instances — no global RNG mutation in cached demo functions.

v1.9.0 — Security Hardening, Lazy Init & Expanded Sprite Library

Capability Details
XSS hardening sanitizeHtml DOM-based sanitizer (replaces regex) strips 12 tag types, all on* event handlers, and javascript: URIs. Applied to tooltip innerHTML, marker popups, and native popups.
Tooltip validation MapWidget constructor and update_tooltip() raise ValueError when the tooltip dict is missing the required "html" key. Shared _validate_tooltip() helper enforces the same rules in both places.
Lazy map initialization Maps in hidden Bootstrap tabs defer WebGL context creation until the tab is first shown. Deferred messages are queued and replayed automatically, preventing wasted GPU contexts and blank-canvas bugs.
Sprite library — 10 dorsal silhouettes ICON_ATLAS and ICON_MAPPING expanded from 3 side-view SVG seals to 10 top-down PNG species: Grey seal, Ringed seal, Harbour seal, Harbour porpoise, Bottlenose dolphin, White-beaked dolphin, Atlantic cod, Baltic herring, Atlantic salmon, European smelt. Species-specific scaling reflects real body size proportions.
sprites/build_atlas.py Sprite build pipeline — drop source PNGs into sprites/src/, run the script, and it auto-generates the atlas PNG and ICON_MAPPING dict.
Error visibility initMap and CDN timeout failures now surface a styled in-map error banner instead of only logging to the console.
Robustness replayDeferredMessages uses per-handler try/catch so one bad message cannot abort the queue. isInVisibleTab walks the full Bootstrap ancestor chain. cloneLayersData performs a 2-level deep clone of transition specs.
Test infrastructure _FakeSession consolidated into shared conftest.py. 10 new XSS/validation tests in test_tooltip_xss.py.

v1.7.0 — Layer Legend Widget, Animation API & Color Ramps

Capability Details
layer_legend_widget() Auto-introspecting deck.gl legend widget — reads active layers at runtime, detects layer type → swatch shape, extracts colors from props/data. Supports manual entries, visibility checkboxes, collapsible header, exclude_layers, label_map.
Layer Gallery auto-legend Tab 1 demo now uses layer_legend_widget(auto_introspect=True) with live updates as layers are toggled.
Color ramp constants 5 new palettes (PALETTE_BLUES, PALETTE_GREENS, PALETTE_REDS, PALETTE_YELLOW_RED, PALETTE_BLUE_WHITE) + 7 short-name aliases (VIRIDIS, OCEAN_DEPTH, etc.).
Client-side animation API animate_prop() + set_animation() for requestAnimationFrame-based visual animations with zero server round-trips.
Performance patterns guide New docs/performance-patterns.md with static/dynamic split pattern and data visualization recipes.

v1.6.2 — Security & Error Handling

Capability Details
Accessor expression validation Regex whitelist + prototype pollution blocklist before new Function() — prevents code injection.
Error logging All silent catch blocks now log console.warn (tooltip, controller, controls, WMS, map instance).
HTML escaping Title and widget ID properly escaped in to_html() export to prevent XSS.
COORDINATE_SYSTEM consolidation Single source of truth in enums.py with backwards-compatible alias.
Input validation encode_binary_attribute() raises TypeError for non-numpy input.

v1.6.1 — Demo Update

Capability Details
Demo gallery expansion All 9 new layer types showcased in Layer Gallery tab with UI toggles.
Updated layer count Demo UI reflects "33 layer helpers" (was 24).
Data generator caching New layer data generators use @functools.lru_cache for efficiency.

v1.6.0 — Complete Layer Coverage (33 Layer Types)

Capability Details
9 new layer helpers grid_cell_layer(), solid_polygon_layer(), a5_layer(), geohash_layer(), h3_cluster_layer(), quadkey_layer(), s2_layer(), tile_3d_layer(), scenegraph_layer().
100% deck.gl coverage shiny_deckgl now provides typed helpers for all 33 deck.gl layer types.
LayerType enum 9 new enum values for type-safe layer type constants.
Demo gallery update Layer Gallery tab showcases all 33 layer types with toggles and live visualization.

v1.5.0 — Enum Types & TypedDict Definitions

Capability Details
12 enum classes ControlPosition, WidgetPlacement, EasingFunction, TransitionType, ControlType, Projection, CoordinateSystem, DrawMode, LayerType, ViewType, LightType, PostProcessShader.
TypedDict definitions Structured type hints for configuration dicts: ControlSpec, WidgetSpec, ViewSpec, LayerSpec, EffectSpec, LightSpec, TooltipSpec, TransitionSpec, ViewState.
H3HexagonLayer fix Correctly normalizes H3 cell IDs to lowercase for deck.gl compatibility.
214 new tests Expanded test suite covering enums, TypedDicts, and edge cases.

v1.4.0 — Partial Updates, Patch Layer & JS Modernisation

Capability Details
partial_update() Push sparse layer patches — only changed properties are sent, avoiding full-stack resend. Auto-serialises DataFrames.
patch_layer() Convenience wrapper to patch a single layer by ID: await widget.patch_layer(session, "my_layer", opacity=0.5).
Tooltip dismiss Map tooltips auto-hide when cursor moves to empty space; Bootstrap switch tooltips no longer persist after toggle.
JS modernisation structuredClone() replaces JSON.parse(JSON.stringify()); all var declarations converted to const/let.
TripsLayer partial fix partial_update now correctly restarts TripsLayer animation after patching.

v1.3.0 — Mesh Parsers, Colour Scales & Library Extraction

Capability Details
parsers.py module parse_shyfem_grd() and parse_shyfem_mesh() — parse SHYFEM .grd finite-element meshes into deck.gl layer-ready data. Auto-detects UTM Zone 33N → WGS84 projection.
custom_geometry() Helper to convert parsed mesh geometry into simple_mesh_layer() kwargs with inline luma.Geometry vertex arrays.
COORDINATE_SYSTEM Enum-style constants mirroring deck.gl's coordinate systems: DEFAULT, LNGLAT, METER_OFFSETS, LNGLAT_OFFSETS, CARTESIAN.
depth_color() Bathymetric blue-gradient RGBA colour function with configurable max depth and alpha.
Colour Scales tab Demo Tab 4 — interactive palette picker (5 palettes × 3 modes), swatch preview, statistics, code examples, and live-coloured bathymetry map.
Demo data factories 7 gallery data factories, fish species colours, SHYFEM helpers, and legend metadata extracted from app.py into reusable library modules.
Demo refactored 10 tabs (was 9); inline helpers extracted into _demo_data.py, _demo_css.py, _version.py, colors.py.

v1.0.0 — Extensions, Clusters & Modular Architecture

Capability Details
Module split 3 000-line components.py monolith split into 8 focused modules with backward-compatible re-export shim.
Extension helpers brushing_extension(), collision_filter_extension(), data_filter_extension(), mask_extension(), clip_extension(), terrain_extension(), fill_style_extension(), path_style_extension() — convenience functions for deck.gl layer extensions.
Cluster layers add_cluster_layer() / remove_cluster_layer() — MapLibre GeoJSON clustering with click-to-zoom, size-stepped circles, and count labels.
Cooperative gestures MapWidget(cooperative_gestures=True) — require Ctrl+scroll to zoom, two-finger drag on mobile.
Style diff set_style(session, url, diff=True) — preserves sources/layers across basemap changes.
Demo Tab 9 Interactive showcase: BrushingExtension, DataFilterExtension, MapLibre clustering, cooperative gestures.

v1.1.0 — IBM Helpers, Trips Animation Module & Audit Fixes

Capability Details
format_trips() Normalise raw [lon, lat] or [lon, lat, time] paths into trips_layer() dict format with auto-timestamps and per-trip property merge.
trips_animation_ui() Reusable Shiny module — Play/Pause/Reset buttons + speed & trail sliders, drop into any sidebar.
trips_animation_server() Wires animation buttons to MapWidget.trips_control(), exposes speed / trail reactive values.
Extension type alias Unified return type for all 8 extension helpers (str | list[str | dict]).
wms_layer() validation Now raises ValueError when the required layers kwarg is omitted.
Thread-safe MPA cache HELCOM GeoJSON loader switched to @functools.lru_cache.
Demo Tab 12 rewired Seal IBM tab now uses trips_animation_ui/server — ~40 lines of boilerplate removed.

v1.2.0 — Effects Module, New Layers & Demo Consolidation

Capability Details
effects.py module 6 helpers for deck.gl lighting & post-processing: ambient_light(), point_light(), directional_light(), sun_light(), lighting_effect(), post_process_effect().
point_cloud_layer() Typed helper for 3-D point cloud rendering.
simple_mesh_layer() Place 3-D OBJ/PLY models on the map.
terrain_layer() Reconstruct mesh from Terrain-RGB height-map tiles.
fp64_extension() Double-precision GPU rendering for extreme zoom levels.
orbit_view() OrbitView — orbit a camera around a 3-D target.
Demo refactored 14 → 11 tabs: Effects merged into 3D Visualisation, Clusters into MapLibre Controls, Extensions into Advanced, Animation into Seal IBM; new Layer Gallery tab (all 33 layer helpers).
JS buildEffects Updated for SunLight support via @@sunLight marker.

Environment & Prerequisites

MapLibre versions: 6.11.2 served, 5.24.0 exported

Shiny apps load MapLibre GL JS 6.11.2. v6 is ESM-only — it ships no UMD/IIFE build — so it is loaded with a dynamic import() rather than a <script src> tag. The module URL is published in an inert <script type="application/json" id="shiny-deckgl-cdn"> block, which a strict script-src Content-Security-Policy allows without 'unsafe-inline'.

Standalone to_html() exports deliberately stay on MapLibre 5.24.0. MapLibre 6 starts its tile worker as a module worker created from a blob URL; on a file:// page the origin is opaque, so that worker cannot resolve its own imports and dies without raising a map error. The deck.gl layers still draw but the basemap never loads — a silent failure. MapLibre 5's classic worker has no such restriction, so exports opened by double-click keep working.

If you serve an export over HTTP rather than opening it from disk, v6 works there too; the pin is in shiny_deckgl._cdn.MAPLIBRE_EXPORT_VERSION.

Note that MapLibre 5.24.0 is the final v5 release and receives no further updates.

  • Python ≥ 3.10 and Shiny ≥ 1.6.3 are required. (Shiny 1.6.2 and later require Python 3.10.)
  • Any standard Python environment works (venv, conda, micromamba, system).

Installation

pip install -e .          # editable install (local package, not on PyPI yet)

For binary transport support (numpy):

pip install -e ".[binary]"

For GeoPandas integration:

pip install -e ".[geopandas]"

For the test suite (no geospatial stack) or the full contributor environment:

pip install -e ".[test]"   # pytest, hypothesis, pytest-benchmark, numpy, pandas, pillow
pip install -e ".[dev]"    # the above plus geopandas and ruff

Or install the published conda package:

micromamba install -c razinka -c conda-forge shiny-deckgl
# or: conda install -c razinka -c conda-forge shiny-deckgl

Note the conda package is named shiny-deckgl (hyphen); the import name is shiny_deckgl (underscore). To build it yourself:

conda build conda.recipe/   # or: micromamba build conda.recipe/

Quick Start

shiny_deckgl-demo

This launches a comprehensive demo app centred on the Baltic Sea with scatter points, WMS layers, controls, drawing tools, markers, popups, terrain, and HTML export.

Usage in your own apps

from shiny import App, ui, reactive, Session, render
from shiny_deckgl import MapWidget, scatterplot_layer, head_includes, CARTO_DARK

map_widget = MapWidget(
    "mymap",
    view_state={"longitude": 21.1, "latitude": 55.7, "zoom": 8},
    tooltip={"html": "<b>Point</b><br/>lon: {0}, lat: {1}"},
    style=CARTO_DARK,
)

app_ui = ui.page_fluid(
    head_includes(),
    map_widget.ui(height="80vh"),
    ui.output_text_verbatim("click_info"),
)

def server(input, output, session: Session):
    @reactive.Effect
    async def _push_layers():
        layers = [scatterplot_layer("pts", [[21.12, 55.70], [21.15, 55.72]])]
        await map_widget.update(session, layers)

    @render.text
    def click_info():
        val = input[map_widget.click_input_id]()
        return str(val) if val else "Click a point…"

my_app = App(app_ui, server)
shiny run my_app.py

Using WMS tile layers

from shiny_deckgl import tile_layer

wms_url = (
    "https://ows.emodnet-bathymetry.eu/wms?"
    "SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap"
    "&FORMAT=image/png&TRANSPARENT=true"
    "&LAYERS=emodnet:mean_atlas_land"
    "&CRS=EPSG:3857&WIDTH=256&HEIGHT=256"
    "&BBOX={bbox-epsg-3857}"
)
layers.append(tile_layer("emodnet-bathy", wms_url))

Color scales

from shiny_deckgl import color_bins, PALETTE_OCEAN

depths = [10.5, 50.2, 120.0, 300.7, 15.3]
colors = color_bins(depths, n_bins=4, palette=PALETTE_OCEAN)
# colors[i] is [R, G, B, 255] for each depth value

HTML export

layers = [scatterplot_layer("pts", [[21.12, 55.70]])]
map_widget.to_html(layers, path="my_map.html", title="Exported Map")

deck.gl legend for overlay layers

from shiny_deckgl import deck_legend_control, scatterplot_layer, arc_layer, heatmap_layer

await widget.set_controls(session, [
    {"type": "navigation", "position": "top-right"},
    deck_legend_control(
        entries=[
            {"layer_id": "ports", "label": "Baltic Ports",
             "color": [65, 182, 196], "shape": "circle"},
            {"layer_id": "routes", "label": "Shipping Routes",
             "color": [255, 140, 0], "color2": [200, 0, 80], "shape": "arc"},
            {"layer_id": "density", "label": "Observation Density",
             "colors": [[0, 25, 0, 255], [0, 209, 0, 255], [255, 255, 0, 255], [255, 0, 0, 255]],
             "shape": "gradient"},
        ],
        position="bottom-right",
        title="Deck.gl Layers",
    ),
])

Widgets and camera transitions

from shiny_deckgl import zoom_widget, compass_widget, fullscreen_widget

await widget.set_widgets(session, [
    zoom_widget(), compass_widget(), fullscreen_widget(),
])

await widget.fly_to(session, longitude=20.0, latitude=55.5, zoom=8, pitch=45)

Codebase Structure

File Purpose
src/shiny_deckgl/map_widget.py MapWidget class — core widget with session methods, HTML export, JSON serialisation.
src/shiny_deckgl/layers.py Generic layer() + 33 typed layer helpers (scatter, arc, trips, grid, point cloud, mesh, terrain, scenegraph, tile3d, …).
src/shiny_deckgl/colors.py Color scales (color_range, color_bins, color_quantiles), palettes, basemap constants.
src/shiny_deckgl/views.py View helpers (map_view, orthographic_view, first_person_view, globe_view, orbit_view).
src/shiny_deckgl/widgets.py 21 deck.gl widget helpers (zoom, compass, fullscreen, timeline, toggle, selector, …) plus two deprecated ones.
src/shiny_deckgl/controls.py MapLibre control helpers, legend, opacity, deck.gl legend.
src/shiny_deckgl/extensions.py 9 extension helpers (brushing, collision filter, data filter, mask, fp64, …).
src/shiny_deckgl/_data_utils.py DataFrame/GeoDataFrame serialisation, binary transport.
src/shiny_deckgl/_transitions.py transition() helper for layer property animations.
src/shiny_deckgl/components.py Backward-compatible re-export shim (imports from split modules).
src/shiny_deckgl/effects.py Effect helpers: lighting_effect(), post_process_effect(), and 4 light-source factories.
src/shiny_deckgl/parsers.py SHYFEM .grd mesh parsers: parse_shyfem_grd() (PolygonLayer data) and parse_shyfem_mesh() (SimpleMeshLayer geometry).
src/shiny_deckgl/app.py Demo app instance — 10-tab sidebar UI showcasing all features.
src/shiny_deckgl/ui.py head_includes() — injects pinned CDN scripts and local JS/CSS.
src/shiny_deckgl/_cdn.py CDN URL constants — single source of truth for all external asset URLs.
src/shiny_deckgl/_version.py Package version — single source of truth.
src/shiny_deckgl/cli.py shiny_deckgl-demo CLI entry point.
src/shiny_deckgl/_demo_data.py Sample Baltic Sea data (ports, routes, MPA GeoJSON) for the demo app.
src/shiny_deckgl/_demo_css.py Marine-themed CSS and UI helpers for the demo app.
src/shiny_deckgl/ibm.py IBM (Individual-Based Model) visual assets, format_trips() data helper, and trips_animation_ui/server Shiny module.
src/shiny_deckgl/resources/deckgl-init.js Frontend: MapLibre init, deck.gl overlay, message handlers, draw tools, popups, terrain.
src/shiny_deckgl/resources/styles.css Minimal layout + tooltip styles for .deckgl-map containers.
conda.recipe/meta.yaml Conda build recipe (version synced with _version.py).
src/shiny_deckgl/_sealmove.py Seal movement simulation engine for the IBM demo tab.
sprites/build_atlas.py Sprite build pipeline — drop source PNGs into sprites/src/, auto-generates atlas PNG and ICON_MAPPING dict.
tests/test_basic.py 1 078 unit tests covering all features.
tests/test_tooltip_xss.py 10 XSS/validation tests covering sanitizeHtml, tooltip validation, and hyphenated field templates.

Performance Patterns

See docs/performance-patterns.md for guidance on:

  • Static / Dynamic Layer Split — use partial_update() for frequently-changing layers to reduce serialisation cost by 80–90 %
  • Categorical Data — use scatterplot_layer with pre-computed colors for discrete data classes

Running Tests

pip install -e ".[dev]"   # or ".[test]" to skip the geospatial tests
pytest tests/ -v

A few suites skip themselves when an optional tool is missing rather than failing: the JS helper tests (tests/_js_harness.py) need node on PATH, and the end-to-end tests (tests/test_e2e_playwright.py) need Playwright with Chromium installed. The end-to-end file is memory-hungry — on a 16 GB machine run it per class rather than all at once.

License

MIT — see LICENSE.

Metadata

Release files for shiny-deckgl 1.12.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for shiny-deckgl 1.12.0
File Size Uploaded
shiny_deckgl-1.12.0.tar.gz 591.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for shiny-deckgl 1.12.0
File Interpreter ABI Platform
shiny_deckgl-1.12.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.0 MB

Release files / shiny_deckgl-1.12.0.tar.gz

Download URL shiny_deckgl-1.12.0.tar.gz
Size 591.7 kB
Tags Source
SHA-256 checksum
How to use checksums
05c42957e3065a6a57a8232505e3ee405303721bf7604628992d578fa6a29417
BLAKE2b-256 checksum
How to use checksums
3d1fe905b0e995a80a46b0ca02b88c208c38cdb0b26012355c7f7a333c6b66f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release files / shiny_deckgl-1.12.0-py3-none-any.whl

Download URL shiny_deckgl-1.12.0-py3-none-any.whl
Size 420.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b9b54d3ba5a877ad65020202d75c85f269cf53a98dc35aec5ebce9d4d3b24204
BLAKE2b-256 checksum
How to use checksums
45ca9181cd2bf71e6dcba5d9d34c6b74e8faff143323c1e6feb065683392a18c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

1.12.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page