# Pydeck Visualizations

[Pydeck visualizations](https://deckgl.readthedocs.io/en/latest/) are supported by JustPy using the `PyDeckFrame` component.

Set the `deck` attribute of the `PyDeckFrame` instance to a [pydeck deck](https://deckgl.readthedocs.io/en/latest/deck.html)

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](https://github.com/streamlit/demo-uber-nyc-pickups)

!!! warning
    You need to obtain a [mapbox api token](https://docs.mapbox.com/help/getting-started/access-tokens/) to have the data render on map tiles
    
```python
# 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)

``` 