Skip to content

Pydeck Visualizations

Pydeck visualizations are supported by JustPy using the PyDeckFrame component.

Set the deck attribute of the PyDeckFrame instance to a pydeck deck

As its name implies, the component inserts a HTML frame in the page which includes the HTML produced by the to_html() method of Deck.

The component also supports a transition_duration attribute. When a visualization changes, this attribute determines the time length of the transition between the old frame and the new frame. The time is designated in seconds and the default is 0.1 seconds.

The example below is based on a streamlit demo app

Warning

You need to obtain a mapbox api token to have the data render on map tiles

# This example is based on a streamlit app: https://github.com/streamlit/demo-uber-nyc-pickups
import justpy as jp

import pandas as pd
import numpy as np
import pydeck as pdk
try:
    import altair as alt
    _has_altair = True
except:
    _has_altair = False


DATE_TIME = "date/time"
DATA_URL = (
    "http://s3-us-west-2.amazonaws.com/streamlit-demo-data/uber-raw-data-sep14.csv.gz"
)


def load_data(nrows):
    data = pd.read_csv(DATA_URL, nrows=nrows)
    lowercase = lambda x: str(x).lower()
    data.rename(lowercase, axis="columns", inplace=True)
    data[DATE_TIME] = pd.to_datetime(data[DATE_TIME])
    return data


data = load_data(100000)
lowercase = lambda x: str(x).lower()
data.rename(lowercase, axis="columns", inplace=True)
data[DATE_TIME] = pd.to_datetime(data[DATE_TIME])


def map_deck(data, lat, lon, zoom):
    deck = (pdk.Deck(
        # https://docs.mapbox.com/help/getting-started/access-tokens/
        api_keys={'mapbox': 'Insert your mapbox api token here'},
        map_provider='mapbox',
        map_style="light",
        initial_view_state={
            "latitude": lat,
            "longitude": lon,
            "zoom": zoom,
            "pitch": 50,
        },
        layers=[
            pdk.Layer(
                "HexagonLayer",
                data=data,
                get_position=["lon", "lat"],
                radius=100,
                elevation_scale=4,
                elevation_range=[0, 1000],
                pickable=True,
                extruded=True,
            ),
        ]
    ))
    return deck


airports = {
    'La Guardia': [40.7900, -73.8700],
    'JFK': [40.6650, -73.7821],
    'Newark': [40.7090, -74.1805]
}

zoom_level_airports = 12
zoom_level_main = 11
midpoint = (np.average(data["lat"]), np.average(data["lon"]))


def create_histogram(hour_selected):
    filtered = data[
        (data[DATE_TIME].dt.hour >= hour_selected) & (data[DATE_TIME].dt.hour < (hour_selected + 1))
        ]
    hist = np.histogram(filtered[DATE_TIME].dt.minute, bins=60, range=(0, 60))[0]
    chart_data = pd.DataFrame({"minute": range(60), "pickups": hist})
    chart = alt.Chart(chart_data).mark_area(
        interpolate='step-after',
    ).encode(
        x=alt.X("minute:Q", scale=alt.Scale(nice=False)),
        y=alt.Y("pickups:Q"),
        tooltip=['minute', 'pickups']
    ).configure_mark(
        opacity=0.5,
        color='red'
    ).properties(
        width='container',
        height=200
    )
    return chart


async def slider_change(self, msg):
    wp = msg.page
    hour = int(self.value)
    wp.all_caption.text = f'All New York City from {hour}:00 to {hour+1}:00'
    hour_data = data[data[DATE_TIME].dt.hour == hour]
    wp.decks['main'].deck = map_deck(hour_data, midpoint[0], midpoint[1], zoom_level_main)
    for airport, coords in airports.items():
        wp.decks[airport].deck = map_deck(hour_data, coords[0], coords[1], zoom_level_airports)
    if _has_altair:
        wp.histogram.chart = create_histogram(self.value)
        wp.histogram_caption.text = f'Breakdown of rides per minute between {hour}:00 and {hour+1}:00'


def pydeck_demo(request):
    wp = jp.QuasarPage(tailwind=True, title='Uber NYC Pickups')
    wp.decks = {}
    slider_div = jp.Div(a=wp, classes='flex ml-4', style='width: 50%; margin-top: 20px')
    jp.Div(text='Select hour of pickup:', a=slider_div, classes='pt-3 text-xl font-bold ')
    s1 = jp.Span(classes='ml-6 mt-1', style='width: 50%',a=slider_div)
    jp.QSlider(classes='w-64', min=0, max=23, a=s1, label=True,
               label_always=True, markers=True, step=1, snap=True, color='red',
               change=slider_change)
    deck_div = jp.Div(classes='flex ml-2', a=wp)
    hour_data = data[data[DATE_TIME].dt.hour == int(0)]
    deck = map_deck(hour_data, midpoint[0], midpoint[1], zoom_level_main)
    all_div = jp.Div(a=deck_div)
    wp.all_caption = jp.Div(text='All New York City from 0:00 to 1:00', a=all_div, style='margin: 10px;', classes='text-xl font-bold')
    wp.decks['main'] = jp.PyDeckFrame(a=all_div, deck=deck, style='margin: 10px; height: 400px; width: 450px', transition_duration=0.5)
    for airport, coords in airports.items():
        airport_deck = map_deck(hour_data, coords[0], coords[1], zoom_level_airports)
        airport_div = jp.Div(a=deck_div)
        jp.Div(text=f'{airport} Airport', a=airport_div, style='margin: 10px;', classes='text-xl font-bold')
        wp.decks[airport] = jp.PyDeckFrame(a=airport_div, deck=airport_deck, style='margin: 10px; height: 400px; width: 250px',
                              transition_duration=0.5)
    if _has_altair:
        chart = create_histogram(0)
        wp.histogram_caption = jp.Div(text='Breakdown of rides per minute between 0:00 and 1:00', a=wp, style='margin-left: 30px;', classes='text-xl font-bold')
        wp.histogram = jp.AltairChart(chart=chart, a=wp, style='padding: 10px; width: 100%;')
    return wp


jp.justpy(pydeck_demo, VEGA=True)