Hello,
I’m want access the ID of a feature in a MVTLayer (or even better would be all of its properties) which is inside a Panel pane. Here is working example (or open it in Colab):
import panel as pn
import pydeck
from functools import partial
pn.extension("deckgl")
view_state = pydeck.ViewState(
latitude = 33.740,
longitude = 35.905,
zoom = 10,
max_zoom = 11.49,
pitch = 45,
bearing = 0
)
layer = pydeck.Layer(
type = 'MVTLayer',
data = "https://raw.githubusercontent.com/bertcoerver/MVTile_tests/main/tpc_folder/{z}/{x}/{y}.pbf",
get_line_color = [0, 255, 0],
get_fill_color = [155, 0, 100],
line_width_min_pixels = 1,
pickable = True,
)
deck = pydeck.Deck(
layers = [layer],
initial_view_state = view_state,
map_style = "light",
)
pn_deck = pn.pane.DeckGL(deck)
def on_map_click(event, deck):
print(deck.layers[0])
print(event.obj.click_state)
print(event.obj.click_state["index"])
# How to find the ID thats shown in the tooltip
pn_deck.param.watch(partial(on_map_click, deck = deck), ["click_state"])
pn.Column(pn_deck)
When I hover over the map I can see the ID in the tooltip. When I click the map, there is a callback function (called on_map_click
) in which I have access to the coordinates of the click, and the Deck.
So, given the variable deck
(which contains the layer) and a coordinate, can I retrieve the corresponding ID (or properties)?
Thanks for any help!
Bert
Reviving this as I’m still interested in getting this working.
In the meantime I have found a “workaround” by using the clicked lat/lon/zoom to determine which tile (i.e. {z}/{x}/{y}.pbf) I’m clicking on, then downloading that specific tile (with a request.get
), and then using the “index” (from click_state) to find the feature, then I can read the features properties. But this seems overly complicated.
In the Deck.GL docs, I can see that the MVTLayer has a method called getRenderedFeature
.
Is there a way I can somehow use this method when there is a click-event and store its output into a Parameter? Any clues on how to implement this?
Found a solution (very happy about this
)
I’ve added a event to the pn.pane.DeckGL
that takes whatever is in the tooltip and puts that into a new Parameter
.
import panel as pn
import param
from panel.custom import JSComponent, Child
class SuperDeckGL(JSComponent):
child: pn.pane.DeckGL = Child(class_=pn.pane.DeckGL)
tooltip = param.String(default="nothing")
_esm = """
export function render({ model }) {
const map_pane = document.createElement("map_pane");
const child = model.get_child("child")
child.addEventListener("click", (ev) => {
var data = child.shadowRoot.querySelector(".deck-tooltip").innerHTML
model.send_msg(data)
});
map_pane.append(child)
return map_pane
}
"""
def _handle_msg(self, msg):
print(msg)
self.param.update(tooltip = str(msg))
json_spec = {
"initialViewState": {
"latitude": 9.05,
"longitude": 0.6,
"pitch": 35,
"zoom": 4,
},
"layers": [
{
"@@type": "MVTLayer",
"getLineColor":[255, 10, 255, 150],
"getFillColor":[0, 255, 0, 0],
"id": "L1",
"data": "https://storage.googleapis.com/fao-cog-data/footprints/L1/{z}/{x}/{y}.pbf",
"pickable": True,
},
],
"mapStyle": "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
"views": [{"@@type": "MapView", "controller": True}],
}
pane_ = pn.pane.DeckGL(
json_spec,
height=300,
width=600,
tooltips={"html": "{properties.fid}"}
)
pane = SuperDeckGL(child=pane_)
pn.serve(
pane,
port=8000 # otherwise this particular MVTLayer gives CORS error.
)
- This is already really convenient for my application. Even better would be if I could figure out how to get the Deck instance so that I can call functions like
getRenderedFeature
on a layer.
- I’m not so familiar with javascript, so not sure if this is the most efficient way to write the
_esm
code (e.g. for now the event seems to trigger twice).
Guess I cheered too soon, would really appreciate some help…
The code above is basically working and doing what I want, but when I try to put it into my application everything seems to break. Especially when I remove the width=600
and replace it with sizing_mode="stretch_width"
. Its like there is some styling logic missing, but I have no clues on how to add that?
I’m seeing the following error in my console:
[Error] Error rendering Bokeh items: – TypeError: undefined is not an object (evaluating 'this.deckGL.redraw') — panel.min.js:174:5109 TypeError: undefined is not an object (evaluating 'this.deckGL.redraw')
[Error] TypeError: undefined is not an object (evaluating 'this.deckGL.redraw')
[Warning] [bokeh 3.7.3] – "panel.models.layout.ColumnView(p1004) wasn't built properly" (bokeh.min.js, line 594)
Can you submit a GitHub issue on Panel? And do you have the latest panel?
Hi @ahuang11 yes I’m on 1.7.2.
So there is no nothing “wrong” with my code? I’m new to these JSComponents, so wasnt sure if this is a bug or if I’m making a mistake.