Custom DeckGL Layers in DeckGL panes

I am making use of the DeckGL pane and finding it very effective.

When I was working with PyDeck one of the things I ended up needing for my application was a custom layer providing level-of-detail based text annotations. This was possible to implement in PyDeck as a custom CompositeLayer type, and the requisite javascript implementation of the layer has been pushed up to the node.js infrastructure (see LOD_text_layer for the full PyDeck implementation).

Now that I am using Panel to integrate deckgl plots into a richer application I am interested in getting the LOD_text_layer working with the Panel DeckGL pane. I’ve dug around a little, and it looks like it might be possible to add the required javascript bundle to __javascript_raw__ in the DeckGLPlot model, but it doesn’t look like I can inject that directly from the DeckGL pane as it is currently implemented. Potentially I can create my own custom subclasses, but I was wondering if I was missing something obvious that would make this easier to arrange.

1 Like

To get started you should be able to inject raw js via a HTML pane.

Something like

raw_js = pn.pane.HTML("""
<script>
...
</script>
""")

If the javascript is available in a file on github you can also add it via something like `pn.config.js_files[“some_name”]=…url…".

It would be nice to use pn.config.js_raw but that would be a feature request for Panel.

for plotly it can be done something like this,

maybe it works for DeckGL pane something like that

from panel.models.deckgl import DeckGLPlot
DeckGLPlot.__javascript_raw__.append(['js_library_or_module_to_load'])
1 Like

Ahh. In panel terminology raw means the actual content.

You can always do

import panel as pn

pn.config.js_files["my-lib"]="js_library_to_load"
pn.config.js_modules["my-module"]="js_module_to_load"

Thanks all, this certainly provides me with some useful paths to try, I have gotten pulled away to some other tasks for bit, but when I get some time I will try these out and see if I can get it working and let you know what the eventual solution turned out to be.

2 Likes

It seems that things are more complicated than I had first hoped. The trick seems to be that the DeckGLPlot model uses a JSONConverter (even when using pydeck I think) to push things through to deck.gl. The catch with that is that the JSONConverter needs to have various functions and classes registered with it (via a configuration dictionary), and in particular that includes classes for custom layer types. Right now the model defines a very specific configuration dict:

      const jsonConverterConfiguration: any = {
        classes: extractClasses(),
        // Will be resolved as `<enum-name>.<enum-value>`
        enumerations: {
          COORDINATE_SYSTEM: (window as any).deck.COORDINATE_SYSTEM,
          GL
        },
        // Constants that should be resolved with the provided values by JSON converter
        constants: {
          Tiles3DLoader
        }
      };
      this.jsonConverter = new (window as any).deck.JSONConverter({
        configuration: jsonConverterConfiguration
      });

This loads some useful constants, and all the layer classes from base deck.gl, but if you want custom javascript functions or custom layers accessible to the JSONConverter (which is possible) then this won’t support it. I believe it might be possible to amend the relevant classes and model to support adding extra classes (and possible extra custom functions), but I admit my typescript is not exactly up to this right now. Do others think this might be a worthwhile thing to try adding to the deck.gl pane, or is it out of scope?

I don’t think this is out of scope at all but I’d have to think a bit about how we can support a custom configuration. Let me know if you have any ideas and feel free to open an issue to discuss.

My initial thought was to have parameters in the python class for custom_js_classes and custom_js_functions (names up for discussion) that take strings of javascript that are then parsed in the same (or a similar) way to the deck module to extract classes (and a similar parser to extract functions for the functions field of the configuration) and add them to the configuration dictionary. Alternatively one could have class_js_files and class_js_modules or similar to use files or modules rather than just passing in raw strings.

I guess one extra advantage of this is that it might also allow using Nebula.gl layers etc. as you could (in theory) inject them by the same approach.

I have circled back around to this again, and am making some progress. A major caveat is that I don’t know typescript, and can mostly only read javascript. Looking through how pydeck handles this – it allows custom libraries (in a global settings dictionary), and then handles that within the createDeck function in the jupyter-widget creation, and that, in turn has the following javascript which loads in extra classes and adds them to the library. Given that some of that looks like it was translated to typescript to handle loading the base deck classes I am hopeful that more can be translated across to at least allow for custom layers. I’m going to try doing that myself to see if it is indeed viable and will try to report back here one way or another.

1 Like