plotly / dash-deck

Bringing deck.gl and pydeck into Dash
https://dash-gallery.plotly.host/dash-deck-explorer/
MIT License
90 stars 19 forks source link

Is it possible to update map in a callback function based on the value of other components? #7

Closed aishwd94 closed 3 years ago

aishwd94 commented 3 years ago

Description

I am trying to update the rendered map through a callback function like this:

layer = pdk.Layer(
    "TripsLayer",
    df,
    get_path="coordinates",
    get_timestamps="timestamps",
    get_color=[253, 128, 93],
    opacity=0.8,
    width_min_pixels=5,
    rounded=True,
    trail_length=600,
    current_time=500,
)

r = pdk.Deck(layers=[layer])
deckgl_map = dash_deck.DeckGL(r.to_json(), id="map-graph", mapboxKey=mapbox_access_token)

@app.callback(
    Output("map-graph", < component_property ??!! >),
    [
        Input("date-picker", "date"),
        Input("trip-slider", "value")
    ],
)
def update_graph(datePicked, sliderValue):
    # use slider-value to filter / modify and update the map 

In my layout, there is a date picker and a slider. I want to update the map based on the values on the slider and the date picker or any other input. However I do not know the syntax to do so or whether it can be done at all. Also, if it is possible, what would be the component_property for the deck component ?

wtglad commented 3 years ago

Any updates on this? This package looks awesome, but I am currently blocked by the same issue.

aishwd94 commented 3 years ago

Hi wtglad, I did not have a full understanding of Dash when I asked this question. The Dash documentation is also quite extensive and can be difficult to navigate.

Here's how I solved it:

  1. Make a div say 'map-container' and the component property to be updated would be its 'children' instead of 'figure'
  2. No change in the update_graph method

                 app.layout = html.Div( 
                              .
                               .
                               .
                            html.Div(
                            id='map-container',
                            className="row div-for-charts bg-grey",
                            children=[
                            ],
                            ),
                               . 
                               .
                               .
                        )

@app.callback(
    Output("map-container",'children'),
    [
        Input("date-picker", "date"),
        Input("trip-slider", "value")
    ],
)
def update_graph(datePicked, sliderValue):
        <return map object here>
wtglad commented 3 years ago

Cool, got it working - thanks!

lperozzi commented 2 years ago

It is possible to have a minimal viable example to achieve this? Ideally with a layout divided into a left layout (4 columns width) where are the controls and a right layout showing the deck map?

Many thanks for your help

damiondoesthings commented 2 years ago

@lperozzi Below is a modified version of the events demo. It adds a slider to change the elevation scale of the map:

"""
This demo shows how to interact with event callbacks 
like clickInfo, hoverInfo, dragStartInfo, etc.
"""
import os
import json

import dash
from dash.dependencies import Input, Output
from dash import dcc
from dash import html
import dash_deck
import pydeck as pdk

# 2014 locations of car accidents in the UK
UK_ACCIDENTS_DATA = (
    "https://raw.githubusercontent.com/uber-common/"
    "deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv"
)
def get_deck(scale):
    # Define a layer to display on a map
    layer = pdk.Layer(
        "HexagonLayer",
        UK_ACCIDENTS_DATA,
        get_position=["lng", "lat"],
        auto_highlight=True,
        elevation_scale=scale,
        pickable=True,
        elevation_range=[0, 3000],
        extruded=True,
        coverage=1,
    )

    # Set the viewport location
    view_state = pdk.ViewState(
        longitude=-1.415,
        latitude=52.2323,
        zoom=6,
        min_zoom=5,
        max_zoom=15,
        pitch=40.5,
        bearing=-27.36,
    )

    map_view = pdk.View("MapView", controller=True)

    # Render
    r = pdk.Deck(
        layers=[layer],
        initial_view_state=view_state,
        views=[map_view],
    )
    return r

# Start building the layout here
styles = {
    "json-output": {
        "overflowY": "scroll",
        "height": "calc(50% - 25px)",
        "border": "thin lightgrey solid",
    },
    "tab": {"height": "calc(98vh - 115px)"},
}

app = dash.Dash(__name__)
server = app.server

app.layout = html.Div(
    [dcc.Slider(0, 20, 5,
               value=10,
               id='my-slider'
    ),
        html.Div(
            id='map-container',
            style={
                "width": "64%",
                "height": "95vh",
                "display": "inline-block",
                "position": "relative",
            },
            children=[
            ],
        ),
    ]
)

@app.callback(
    Output('map-container','children'),[Input('my-slider', 'value')]
)
def update_graph(scale):
    return dash_deck.DeckGL(
                    get_deck(scale).to_json(),
                    id="deck",
                    tooltip=True,
                )

if __name__ == "__main__":
    app.run_server(debug=True)