trame-deckgl OrthographicView

Hi,

is it possible to use the trame-deckgl widget to plot an overlay of scatter points on image data?
I tried both the trame plotly and image-tools widgets for this purpose, but none of them seem very performant when data sizes become large. The example provided and a browse through the codebase of the trame-deckgl widget does not suggest that orthographic view plots are supported? I would have expected that, similar to Plotly, the entire API of pydeck would be supported through the widget to get deckgl plots in the frontend?

Below is a minimal example in pydeck of what I would want to do in Trame:

import pydeck as pdk
import numpy as np

# ---- Parameters ----
width, height = 3840, 2160

N = 10_000
x = np.random.uniform(0, width, N)
y = np.random.uniform(0, height, N)
colors = np.random.randint(0, 255, size=(N, 3))

data = [
    {"position": [float(x[i]), float(y[i])], "color": colors[i].tolist()}
    for i in range(N)
]

# ---- Layers ----
layers = [
    # Bitmap background
    pdk.Layer(
        "BitmapLayer",
        data=None,
        image="https://upload.wikimedia.org/wikipedia/commons/3/3f/Fronalpstock_big.jpg",
        bounds=[0, 0, width, height],
        desaturate=0,
        opacity=1.0,
    ),
    # Scatter overlay
    pdk.Layer(
        "ScatterplotLayer",
        data=data,
        get_position="position",
        get_fill_color="color",
        get_radius=4,
        radius_min_pixels=2,
        pickable=True,
    ),
]

# ---- Orthographic View ----
ORTHO_VIEW = {"@@type": "OrthographicView", "controller": True}

view_state = {
    "target": [width / 2, height / 2, 0],
    "zoom": 0.5,
}

# ---- Deck ----
deck = pdk.Deck(
    layers=layers,
    initial_view_state=view_state,
    views=[ORTHO_VIEW],
    map_provider=None,
    tooltip={"text": "x: {position[0]}, y: {position[1]}"},
)

deck.show()

Thank you for looking into this!

Here is a working example that you can execute directly if you have uv installed.

#!/usr/bin/env -S uv run --script
#
# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "trame",
#     "trame-deckgl",
#     "pydeck",
# ]
# ///
import numpy as np
import pydeck as pdk
from trame.app import TrameApp
from trame.ui.html import DivLayout
from trame.widgets import client, deckgl


class PyDeckApp(TrameApp):
    def __init__(self, server=None):
        super().__init__(server)
        self._build_ui()
        self._build_viz()

    def _build_viz(self):
        width, height = 3840, 2160

        N = 10_000
        x = np.random.uniform(0, width, N)
        y = np.random.uniform(0, height, N)
        colors = np.random.randint(0, 255, size=(N, 3))

        data = [
            {"position": [float(x[i]), float(y[i])], "color": colors[i].tolist()}
            for i in range(N)
        ]

        # ---- Layers ----
        layers = [
            # Bitmap background
            pdk.Layer(
                "BitmapLayer",
                data=None,
                image="https://upload.wikimedia.org/wikipedia/commons/3/3f/Fronalpstock_big.jpg",
                bounds=[0, 0, width, height],
                desaturate=0,
                opacity=1.0,
            ),
            # Scatter overlay
            pdk.Layer(
                "ScatterplotLayer",
                data=data,
                get_position="position",
                get_fill_color="color",
                get_radius=4,
                radius_min_pixels=2,
                pickable=True,
            ),
        ]

        # ---- Orthographic View ----
        ORTHO_VIEW = {"@@type": "OrthographicView", "controller": True}

        view_state = {
            "target": [width / 2, height / 2, 0],
            "zoom": 0.5,
        }

        # ---- Deck ----
        deck = pdk.Deck(
            layers=layers,
            initial_view_state=view_state,
            views=[ORTHO_VIEW],
            map_provider=None,
            tooltip={"text": "x: {position[0]}, y: {position[1]}"},
        )
        self.ctx.deck.update(deck)

    def _build_ui(self):
        with DivLayout(self.server) as self.ui:
            client.Style("html,body{padding:0;margin:0;}")
            deckgl.Deck(ctx_name="deck", style="width:100%;height:100vh;")


def main():
    app = PyDeckApp()
    app.server.start()


if __name__ == "__main__":
    main()

Fantastic, thanks for the swift reply!

1 Like