From d9dbb88966d6cf677239dd416b6b7e037efb6916 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 24 Aug 2023 01:35:06 +0000 Subject: [PATCH 1/5] #5790: fix missing deckgl api key --- panel/pane/deckgl.py | 2 +- panel/tests/pane/test_deckgl.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/panel/pane/deckgl.py b/panel/pane/deckgl.py index a4ccb77646..89765aeaab 100644 --- a/panel/pane/deckgl.py +++ b/panel/pane/deckgl.py @@ -209,7 +209,7 @@ def _transform_object(self, obj) -> Dict[str, Any]: tooltip = self.tooltips else: data = dict(self.object.__dict__) - mapbox_api_key = data.pop('mapbox_key', self.mapbox_api_key) + 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 diff --git a/panel/tests/pane/test_deckgl.py b/panel/tests/pane/test_deckgl.py index 981270c941..90a57cd474 100644 --- a/panel/tests/pane/test_deckgl.py +++ b/panel/tests/pane/test_deckgl.py @@ -159,3 +159,11 @@ 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" From 35e3d673ef6fa10279efbb1fb7e2a9938811e7db Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 24 Aug 2023 01:58:11 +0000 Subject: [PATCH 2/5] #5790: fix min_zoom/ max_zoom=None issue --- panel/pane/deckgl.py | 25 +++++++++++++++++-------- panel/tests/pane/test_deckgl.py | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/panel/pane/deckgl.py b/panel/pane/deckgl.py index 89765aeaab..0b5dcfed2a 100644 --- a/panel/pane/deckgl.py +++ b/panel/pane/deckgl.py @@ -196,6 +196,22 @@ def _update_sources(cls, json_data, sources): sources.append(cds) layer['data'] = sources.index(cds) + 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 + } + 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 @@ -208,14 +224,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', "") 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} + data, tooltip, mapbox_api_key = self._transform_deck_object(self.object) # Delete undefined width and height for view in data.get('views', []): diff --git a/panel/tests/pane/test_deckgl.py b/panel/tests/pane/test_deckgl.py index 90a57cd474..918945c500 100644 --- a/panel/tests/pane/test_deckgl.py +++ b/panel/tests/pane/test_deckgl.py @@ -167,3 +167,19 @@ def test_pydeck_mapbox_api_key_issue_5790(document, comm): 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 From eb233b54dbf605024aeb0f35c3e557d627cf452b Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 24 Aug 2023 02:35:36 +0000 Subject: [PATCH 3/5] #5790: fix pydeck.types.String serialization issue --- panel/pane/deckgl.py | 17 +++++++++++++++++ panel/tests/pane/test_deckgl.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/panel/pane/deckgl.py b/panel/pane/deckgl.py index 0b5dcfed2a..698ffa8a1c 100644 --- a/panel/pane/deckgl.py +++ b/panel/pane/deckgl.py @@ -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 @@ -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 @@ -196,6 +199,17 @@ 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 @@ -210,6 +224,9 @@ def _transform_deck_object(self, obj): 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]: diff --git a/panel/tests/pane/test_deckgl.py b/panel/tests/pane/test_deckgl.py index 918945c500..3fccbd1b1a 100644 --- a/panel/tests/pane/test_deckgl.py +++ b/panel/tests/pane/test_deckgl.py @@ -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 @@ -183,3 +185,32 @@ def test_pydeck_no_min_max_zoom_issue_5790(document, comm): 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) From edb106a5ed58ac0328bd87ca812879714f6b9b21 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Thu, 24 Aug 2023 11:40:48 +0200 Subject: [PATCH 4/5] Fix array unpacking --- panel/models/data.ts | 9 +++++++-- panel/pane/deckgl.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/panel/models/data.ts b/panel/models/data.ts index 21b2f9d460..2b7d8af1ca 100644 --- a/panel/models/data.ts +++ b/panel/models/data.ts @@ -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++) { @@ -14,13 +15,17 @@ 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) item['_index'] = i data.push(item) } + console.log(data) return data } diff --git a/panel/pane/deckgl.py b/panel/pane/deckgl.py index 698ffa8a1c..fc8ff0c1b1 100644 --- a/panel/pane/deckgl.py +++ b/panel/pane/deckgl.py @@ -124,7 +124,7 @@ class DeckGL(ModelPane): 'view_state': 'viewState', 'tooltips': 'tooltip' } - _pydeck_encoders_are_added: ClassVar[bool]=False + _pydeck_encoders_are_added: ClassVar[bool] = False _updates: ClassVar[bool] = True @@ -208,7 +208,7 @@ def _add_pydeck_encoders(cls): def pydeck_string_encoder(obj, serializer): return obj.value - Serializer._encoders[String]=pydeck_string_encoder + Serializer._encoders[String] = pydeck_string_encoder def _transform_deck_object(self, obj): data = dict(obj.__dict__) From 3d2dda78b61c7a5f76968f9e8e77483c761c3539 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Thu, 24 Aug 2023 11:48:02 +0200 Subject: [PATCH 5/5] Remove stray log statement --- panel/models/data.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/panel/models/data.ts b/panel/models/data.ts index 2b7d8af1ca..07dc1994d0 100644 --- a/panel/models/data.ts +++ b/panel/models/data.ts @@ -25,7 +25,6 @@ export function transform_cds_to_records(cds: ColumnDataSource, addId: boolean = item['_index'] = i data.push(item) } - console.log(data) return data }