✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🧬 Learn how to build RNA-Seq data apps with Python & Dash. Register for the May 20 Webinar!

📣 Initial release of Dash Deck, a library for rendering webgl & 3D maps with pydeck and deck.gl in Dash

Coming back to make posts, this library is good and has its high potential use of ‘Smart Cities’ stuff.

hope ppl will find this library and support you guys.

1 Like

Thanks @xhlu

I have just worked through the night exploring dash-deck, coding like a madman. This is so cool.

I have a question for you regarding custom tooltips for dash-deck:

can you get custom data for the tooltip as json “properties.blah” for a pydeck GeoJsonLayer??

I’ve managed to get custom tooltip data from a dataframe in a different example in the Globeview as per the example:

tooltip={“text”: “{name}, {primary_fuel} plant, {capacity_mw}MW, {country_long}”}

Where the templated values are columns in the dataframe. But in another example I’m working on which is a PolygonLayer and GeoJsonLayer (essentially the “GeoJsonLayer” demo), I haven’t been able to use a dataframe. So I’m using a feature collection of json polygons and I have custom data in this feature collection, that I want to extract, something like:

tooltip={“text”: “{properties.pop_est}”}

I’ve tried no-end of combinations, studied the pydeck tooltip and your readme, but I just can’t get it to work. If I simply set:

tooltip=True

I definitely get data and I can see all the feature properties I want to access. I’m totally new to pydeck and I know this is a 0.0.1 release so not sure if it is a known issue. If there is another way (such as using a dataframe instead of a geojson feature collection, please let me know). Screenshot attached.

Also FYI, if anyone wants to see a public live demo, I’ve got 80 different datasets running using the Geojsonlayer dash-deck so you can explore how it looks. Quite fun flying around etc.

www.worldatlas2.org (JIGSAW button once you’ve loaded a dataset)

Thanks
Dan

2 Likes

Hi Dan! Super happy to hear you enjoy using the library! Your graph looks amazing btw :slight_smile:

The tooltip mechanism is a feature that was added by pydeck, and in dash-deck we are directly copying the makeTooltip utility function that they implemented.

If you take a look at the source code of widgetTooltip.js for pydeck, they are replacing the values in the template with the exact object key, so something like properties.pop_est will not work since it’s not a key of the data object you are passing to tooltip. If you think that’s something that’d be interesting to add to pydeck, feel free to make an issue on their repo and add a pydeck tag.


One workaround I’d suggest would be to flatten your object if that’s possible. For example, if your data is a python dictionary that looks like this:

data = {
  "name": ...,
  "primary_fuel": ...,
  "properties": {
    "pop_est": ...,
    ...
  }
}

then, before feeding it to pydeck, you can modify it to be:

data = {
  "name": ...,
  "primary_fuel": ...,
  "properties_pop_est": ...,
  "properties_...": ...,
  "..."
}

then you would pass that directly to tooltip:

tooltip={“text”: “{properties_pop_est}”}

Let me know if that works!

1 Like

@xhlu… you are my hero :smiley:

Thank you so much. Your work-around has done the trick!

I now have a custom multi-value tooltip for a Geojson polygon extrusion! I haven’t dared look into formatting but you know, I can live without d3 level number formatting for now.

To any others that want to try this kind of chart (pydeck polygon layer, pydeck geojson layer) I can share a few workarounds I’ve done to get it working using a json feature collection (with geometry) and no dataframe input.

Colouring individual features with dash-deck "GeoJsonLayer"
This has a few quirky parse issues. I got it working trial and error to colour individual features, with some pain. I’ll quickly explain.

First, from the dash deck demo, this looks like:

geojson = pydeck.Layer(
“GeoJsonLayer”,
DATA_URL,
opacity=0.8,
stroked=False,
filled=True,
extruded=True,
wireframe=True,
get_elevation=“properties.valuePerSqm / 20”,
get_fill_color="[255, 255, properties.growth * 255]",
get_line_color=[255, 255, 255],
)

The get_fill_color is what you want. Now you can set the RGB values manually to something like:

get_fill_color=[255, 255, 255]

But of course this will colour ALL your features the same. If you want to distinguish them, if you look at the example, it’s actually all in text, with a json property in there, indicating you can manipulate the RGB values for the parser.

get_fill_color="[255, 255, properties.growth * 255]"

Note this is a string. In the case of the dash deck demo (above) this does work, and the properties.growth value is parsed for every feature, but in every case it pushes the 255 value up out of range, which results in the actual colour output [255,255,0] actually displayed. Now at this point you might think (as I did): sweet as, I’ll just construct a nicely formatted string of RGB values as a custom property for each feature. i.e.

in the feature collection: properties.color = “[123,123,123]”

in the pydeck layer def: get_fill_color=“properties.color”

Didn’t work. I then tried returning an invidual string for each RGB in the string hoping it would be parsed like:

get_fill_color="[properties.red, properties.green, properties.blue]"

I was sure this would work. It didn’t. The parser is particular and YOU MUST multiply each json feature property value by a number inside the string. So I solved it with this:

get_fill_color="[properties.red*1, properties.green*1, properties.blue*1]"

Tooltip
Just to share how I got this working with help from @xhlu. Yes all you need to do is add new key/value pairs to your geojson feature collection at the feature level. i.e pull any properties key/values you need up one level.

for i in range(0, len(gj[‘features’])):
gj[‘features’][i][‘my-new-key’] = blah

Then you can reference the keys directly in the DeckGL initialisation code block, and this should magically work for all your features by returning the associated value for that key :slight_smile:

dash_deck.DeckGL(r.to_json(),
id=“deck-gl”,
mapboxKey=r.mapbox_key,
#style={“background-color”: ‘#b0dff7’},
#tooltip=True,
tooltip={“text”: “{my-new-key}” },
)

Cheers
Dan

2 Likes

That looks amazing! It’s a really neat idea to leverage the 3D capabilities of deck.gl to building such “geo-bar charts” :open_mouth:

I think the Dash community would love it if you share it as a Show & Tell post. Source code and hosted demo (e.g. on Dash Enterprise or Heroku) would be appreciated!

1 Like

Already there @xhlu, here! (with link to publicly hosted site!)

I’ll keep the community updated on that thread as I progress with further implementation.

You can run the graphs live and explore 80 datasets. Using Gunicorn as WSGI HTTP server and I’ve spun it up on Heroku using a couple of 2-x dynos. Seems to be holding up so far.

Would love feedback, thoughts, ideas. Much more on the way!

Dan.

2 Likes

I completely missed the post, thank you for sharing there! There’s so many things to do in the app, I have yet to fully explore it!

Have you thought of sharing it on reddit.com/r/dataisbeautiful? I think they would love it over there :slight_smile:

1 Like

Thanks @xhlu . Yes I think that is a great idea on the subreddit (I’m on it but have never posted). I’ve been working hard to get a few more things implemented, and then I think I will do it!

Choropleth geo-bar-graph now live

Hot off the press. With extreme pain, I’ve managed to turn the geo-bar-graph into a choropleth aswell based on the selected colorwheel style from the underlying choroplethmapbox plotly graph object. I had to scrape a colour array from the map state, and implement my own linear color interpolation algorithm. It’s just about killed me but I think it’s working ‘well enough’.

You can change the colour scheme in settings to try out different ones!
WORLD ATLAS 2.0 (14)

1 Like

@xhlu @chriddyp

Guys I’ve managed to get the experimental globe view running as a 3d choropleth map!

This mimics the colorwheel palette of the main choropleth_mapbox graph object. I’ve built a custom linear colour interpolation algorithm based on a colour array I can extract from the graph object figure state. Got it working for any dataset, with hover data!

Still have a few json issues with Americas, but it’s about 95% there :slight_smile:

WORLD ATLAS 2.0 (19)

WORLD ATLAS 2.0 (18)

3 Likes

That looks amazing, I think that’d make a neat add-on library to go along dash-deck!

Awesome extension, so much potential.

Has anyone had success changing the basemap?

Assume the following data passed to deck

{
    "initialViewState": {
        "longitude": -122.45,
        "latitude": 37.8,
        "zoom": 6.6
    },
    "views": [
        {
            "@@type": "MapView",
            "mapStyle": "https://basemaps.cartocdn.com/gl/dark-matter-nolabels-gl-style/style.json"
        }
    ],
    "layers": [
        {
            "@@type": "TextLayer",
            "getColor": [0,255,255],
            "data": [
                {
                    "position": [ -122.45, 37.8 ],
                    "text": "Hello World"
                }
            ]
        }
    ]
}

This example works fine on the deck playground ( https://deck.gl/playground/ ), however the same basemap swap doesn’t work in dash_deck. Any hints on how to change the basemap to a non-mapbox source?

I added extensive testing for mapbox styles but not other styles. Since the library is based on pydeck, I’d recommend checking out how they do it, and use the resultingpdkobject as input to the dash deck. You can check out the explorer for more examples involving pydeck.

Hello,

Thanks so much for this project, it’s really awesome!
I wonder if there’s a way to dynamically get the current ViewState as the user pans throughout the map. I’d like to load a fairly large amount of data and it would be nice to render it dynamically.

Thanks!

Has anyone gotten figure animation to work with pydeck/dash-deck as shown in the deck.gl trips layer example? There is probably some way to do it with dcc.interval and callbacks, but I imagine that would be slower than desirable. Maybe there some way to write some client-side js to run the animation loop?

@karosc I think that the simplest approach would really be dcc.Interval, and you are right that it will be slow once it’s hosted on a server. If you want to accomplish it on the clientside, one possible way would be a custom component using dash-component-boilerplate and reproduce that behavior by using the dash-deck npm package.

Another hypothetical way would be to use clientside callback to assign a loop that would call an animate function with a loop, which in this case might work since you would be able to access window.requestAnimationFrame. Moreover, implementing the setTime function using pure JavaScript might be less elegant without hooks (i.e. you will have to get the element by id and manually set its currentTime prop)

@nicholas-m Sorry for the late response. In the API reference you will find dragStartEventand dragEndEvent, which isn’t the same thing as the view state but might be able to give you enough information about where the user has moved (e.g. through deltas).

Thanks for getting back to me! That makes sense to me and I will look into doing a clientside callback. On another topic, I am having trouble positioning the deck map well. Like kforris above, I want my map to be located within a specific div in my document, but it will not follow the document flow because the deck objects come out in an absolute position. Is there any way for these deck maps to have relative positioning so I don’t need to worry about repositioning them if I add more content above?

You will need to wrap it inside a html div (container):

deck_container = html.Div(
    dash_deck.DeckGL(
        r.to_json(), id="deck-gl", tooltip=TOOLTIP_TEXT, mapboxKey=r.mapbox_key
    ),
    style={"height": "400px", "width": "100%", "position": "relative"},
)

See this demo I just added that uses dash-deck inside a bootstrap container:

That did the trick! Not sure why I couldn’t get it working before, but your example was exactly what I needed. Thanks so much!

@xhlu I am accessing the data attribute of my DeckGL object in a clientside callback and for some reason, the returned object is a stringified json rather than a js object which makes it slow to manipulate as I have to parse it then re-stringify it. Is there a reason why this data attribute is stored as a string rather than an object?