Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DeckGL TextLayer issues #5427

Merged
merged 5 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions panel/models/data.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {ColumnDataSource} from "@bokehjs/models/sources/column_data_source";


export function transform_cds_to_records(cds: ColumnDataSource, addId: boolean = false, start: number = 0): any {
const data: any = []
const columns = cds.columns()
const cdsLength = cds.get_length()
if (columns.length === 0||cdsLength === null)
if (columns.length === 0 || cdsLength === null)
return []

for (let i = start; i < cdsLength; i++) {
Expand All @@ -14,7 +15,10 @@ export function transform_cds_to_records(cds: ColumnDataSource, addId: boolean =
const shape = (array[0] == null || array[0].shape == null) ? null : array[0].shape;
if ((shape != null) && (shape.length > 1) && (typeof shape[0] == "number"))
item[column] = array.slice(i*shape[1], i*shape[1]+shape[1])
else
else if (array.length != cdsLength && (array.length % cdsLength === 0)) {
const offset = array.length / cdsLength
item[column] = array.slice(i*offset, i*offset+offset)
} else
item[column] = array[i]
}
if (addId)
Expand Down
42 changes: 34 additions & 8 deletions panel/pane/deckgl.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import numpy as np
import param

from bokeh.core.serialization import Serializer
from bokeh.models import ColumnDataSource
from pyviz_comms import JupyterComm

Expand Down Expand Up @@ -123,6 +124,8 @@ class DeckGL(ModelPane):
'view_state': 'viewState', 'tooltips': 'tooltip'
}

_pydeck_encoders_are_added: ClassVar[bool] = False

_updates: ClassVar[bool] = True

priority: ClassVar[float | bool | None] = None
Expand Down Expand Up @@ -196,6 +199,36 @@ def _update_sources(cls, json_data, sources):
sources.append(cds)
layer['data'] = sources.index(cds)

@classmethod
def _add_pydeck_encoders(cls):
if cls._pydeck_encoders_are_added or 'pydeck' not in sys.modules:
return

from pydeck.types import String
def pydeck_string_encoder(obj, serializer):
return obj.value

Serializer._encoders[String] = pydeck_string_encoder

def _transform_deck_object(self, obj):
data = dict(obj.__dict__)
mapbox_api_key = data.pop('mapbox_key', "") or self.mapbox_api_key
deck_widget = data.pop('deck_widget', None)
if isinstance(self.tooltips, dict) or deck_widget is None:
tooltip = self.tooltips
else:
tooltip = deck_widget.tooltip
data = {k: v for k, v in recurse_data(data).items() if v is not None}

if "initialViewState" in data:
data["initialViewState"]={
k:v for k, v in data["initialViewState"].items() if v is not None
}

self._add_pydeck_encoders()

return data, tooltip, mapbox_api_key

def _transform_object(self, obj) -> Dict[str, Any]:
if self.object is None:
data, mapbox_api_key, tooltip = {}, self.mapbox_api_key, self.tooltips
Expand All @@ -208,14 +241,7 @@ def _transform_object(self, obj) -> Dict[str, Any]:
mapbox_api_key = self.mapbox_api_key
tooltip = self.tooltips
else:
data = dict(self.object.__dict__)
mapbox_api_key = data.pop('mapbox_key', self.mapbox_api_key)
deck_widget = data.pop('deck_widget', None)
if isinstance(self.tooltips, dict) or deck_widget is None:
tooltip = self.tooltips
else:
tooltip = deck_widget.tooltip
data = {k: v for k, v in recurse_data(data).items() if v is not None}
data, tooltip, mapbox_api_key = self._transform_deck_object(self.object)

# Delete undefined width and height
for view in data.get('views', []):
Expand Down
55 changes: 55 additions & 0 deletions panel/tests/pane/test_deckgl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

pydeck_available = pytest.mark.skipif(pydeck is None, reason="requires pydeck")

from bokeh.core.serialization import Serializer

from panel.models.deckgl import DeckGLPlot
from panel.pane import DeckGL, PaneBase, panel

Expand Down Expand Up @@ -159,3 +161,56 @@ def test_deckgl_insert_layer(document, comm):
assert cds1.data['b'] is b_vals
assert np.array_equal(cds2.data['b'], np.array([3, 9]))
assert np.array_equal(cds2.data['c'], np.array([1, 3]))

@pydeck_available
def test_pydeck_mapbox_api_key_issue_5790(document, comm):
deck_wo_key = pydeck.Deck()
pane_w_key = DeckGL(deck_wo_key, mapbox_api_key="ABC")

model = pane_w_key.get_root(document, comm=comm)
assert model.mapbox_api_key == "ABC"

@pydeck_available
def test_pydeck_no_min_max_zoom_issue_5790(document, comm):
state_w_no_min_max_zoom = {
"latitude": 37.7749,
"longitude": -122.4194,
"zoom": 10,
"bearing": 0,
"pitch": 0,
}
view_state = pydeck.ViewState(**state_w_no_min_max_zoom)
deck = pydeck.Deck(initial_view_state=view_state)
pane = DeckGL(deck)

model = pane.get_root(document, comm=comm)
assert model.initialViewState == state_w_no_min_max_zoom

@pydeck_available
def test_pydeck_type_string_can_be_serialized_issue_5790(document, comm):
serializer = Serializer(references=document.models.synced_references)
data = [
{
"name": "24th St. Mission (24TH)",
"code": "24",
"address": "2800 Mission Street, San Francisco CA 94110",
"entries": 12817,
"exits": 12529,
# "coordinates": [-122.418466, 37.752254]
}
]


layer = pydeck.Layer(
"TextLayer",
data,
get_text_anchor=pydeck.types.String("middle"),
get_alignment_baseline=pydeck.types.String("center"),
size_units = pydeck.types.String("meters") # <--- The key addition to switch to meters as the units.
)
deck = pydeck.Deck(layers=[layer])
pane = DeckGL(deck)

model = pane.get_root(document, comm=comm)
serializer.serialize(model)
assert Serializer._encoders.pop(pydeck.types.String)
Loading