Skip to content

Commit a8b925b

Browse files
committed
Enhance build_map_of_features method in DataUI for improved parameter handling and flexibility; update map feature updates to prevent reactive cycles
1 parent 9ee8044 commit a8b925b

1 file changed

Lines changed: 90 additions & 22 deletions

File tree

dvue/dataui.py

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,25 @@ def _get_map_catalog(self):
511511
dfx = self._dfcat
512512
return dfx
513513

514-
def build_map_of_features(self, dfmap, crs):
514+
def build_map_of_features(
515+
self,
516+
dfmap,
517+
crs,
518+
show_color_by=None,
519+
color_by=None,
520+
non_selection_alpha=None,
521+
point_size=None,
522+
):
523+
# Fall back to current param values when called without explicit arguments (e.g. from __init__)
524+
if show_color_by is None:
525+
show_color_by = self.show_map_colors
526+
if color_by is None:
527+
color_by = self.map_color_category
528+
if non_selection_alpha is None:
529+
non_selection_alpha = self.map_non_selection_alpha
530+
if point_size is None:
531+
point_size = self.map_point_size
532+
515533
tooltips = self._dataui_manager.get_tooltips()
516534
# if station_id column is defined then consolidate the self._dfcat into a single row per station
517535
# this is useful when we have multiple rows per station
@@ -531,9 +549,9 @@ def build_map_of_features(self, dfmap, crs):
531549
except Exception as e:
532550
logger.error(f"Error building map of features: {e}")
533551
self._map_features = gv.Points(dfmap, crs=crs)
534-
if self.show_map_colors:
552+
if show_color_by:
535553
self._map_features = self._map_features.opts(
536-
color=dim(self.map_color_category).categorize(
554+
color=dim(color_by).categorize(
537555
self._dataui_manager.get_name_to_color(), default="blue"
538556
)
539557
)
@@ -543,23 +561,23 @@ def build_map_of_features(self, dfmap, crs):
543561
self._map_features = self._map_features.opts(
544562
opts.Points(
545563
tools=["tap", hover, "lasso_select", "box_select"],
546-
nonselection_alpha=self.map_non_selection_alpha, # nonselection_color='gray',
547-
size=10,
564+
nonselection_alpha=non_selection_alpha,
565+
size=point_size,
548566
)
549567
)
550568
elif isinstance(self._map_features, gv.Path):
551569
self._map_features = self._map_features.opts(
552570
opts.Path(
553571
tools=["tap", hover, "lasso_select", "box_select"],
554-
nonselection_alpha=self.map_non_selection_alpha, # nonselection_color='gray',
572+
nonselection_alpha=non_selection_alpha,
555573
line_width=2,
556574
)
557575
)
558576
elif isinstance(self._map_features, gv.Polygons):
559577
self._map_features = self._map_features.opts(
560578
opts.Polygons(
561579
tools=["tap", hover, "lasso_select", "box_select"],
562-
nonselection_alpha=self.map_non_selection_alpha, # nonselection_color='gray',
580+
nonselection_alpha=non_selection_alpha,
563581
)
564582
)
565583
else:
@@ -576,6 +594,9 @@ def update_map_features(
576594
query,
577595
filters,
578596
selection,
597+
map_default_span,
598+
map_non_selection_alpha,
599+
map_point_size,
579600
):
580601
"""Update the map features based on the selection in the table or filters or query. Also updates if the color or marker by columns are changed"""
581602
query = query.strip()
@@ -613,9 +634,17 @@ def update_map_features(
613634
str_stack = full_stack()
614635
logger.error(str_stack)
615636
notifications.error(f"Error while fetching data for {str_stack}", duration=0)
616-
self.map_color_category = color_by
617-
self.show_map_colors = show_color_by
618-
self._map_features = self.build_map_of_features(current_view, self._crs)
637+
# Pass values directly to build_map_of_features instead of assigning to self params,
638+
# which would fire param events on the same params pn.bind is watching and create a
639+
# reactive cycle that Panel would suppress.
640+
self._map_features = self.build_map_of_features(
641+
current_view,
642+
self._crs,
643+
show_color_by=show_color_by,
644+
color_by=color_by,
645+
non_selection_alpha=map_non_selection_alpha,
646+
point_size=map_point_size,
647+
)
619648
if isinstance(self._map_features, gv.Points):
620649
if show_marker_by:
621650
self._map_features = self._map_features.opts(
@@ -627,7 +656,7 @@ def update_map_features(
627656
self._map_features = self._map_features.opts(marker="circle")
628657
with param.discard_events(self._station_select):
629658
self._map_features = self._map_features.opts(
630-
default_span=self.map_default_span, # for max zoom this is the default span in meters
659+
default_span=map_default_span, # for max zoom this is the default span in meters
631660
selected=current_selection,
632661
)
633662
return self._map_features
@@ -983,18 +1012,57 @@ def create_view(self, title="Data User Interface"):
9831012
self.param.map_point_size,
9841013
self.param.query,
9851014
)
986-
self._map_function = hv.DynamicMap(
987-
pn.bind(
988-
self.update_map_features,
989-
show_color_by=self.param.show_map_colors,
990-
color_by=self.param.map_color_category,
991-
show_marker_by=self.param.show_map_markers,
992-
marker_by=self.param.map_marker_category,
993-
query=self.param.query,
994-
filters=self.display_table.param.filters,
995-
selection=self.display_table.param.selection,
996-
)
1015+
# Use HoloViews streams.Params instead of pn.bind so that ALL param
1016+
# changes (including color/marker category) are routed through
1017+
# HoloViews' own rendering pipeline. pn.bind only triggers a Panel
1018+
# pane swap; it does NOT instruct Bokeh to recreate the
1019+
# CategoricalColorMapper/marker glyph, so color/marker opts changes
1020+
# appear to have no effect. streams.Params guarantees a full
1021+
# HoloViews renderer refresh on every param change.
1022+
_self_stream = streams.Params(
1023+
parameterized=self,
1024+
parameters=[
1025+
"show_map_colors",
1026+
"map_color_category",
1027+
"show_map_markers",
1028+
"map_marker_category",
1029+
"query",
1030+
"map_default_span",
1031+
"map_non_selection_alpha",
1032+
"map_point_size",
1033+
],
9971034
)
1035+
_table_stream = streams.Params(
1036+
parameterized=self.display_table,
1037+
parameters=["filters", "selection"],
1038+
)
1039+
1040+
def _map_callback(
1041+
show_map_colors,
1042+
map_color_category,
1043+
show_map_markers,
1044+
map_marker_category,
1045+
query,
1046+
map_default_span,
1047+
map_non_selection_alpha,
1048+
map_point_size,
1049+
filters,
1050+
selection,
1051+
):
1052+
return self.update_map_features(
1053+
show_color_by=show_map_colors,
1054+
color_by=map_color_category,
1055+
show_marker_by=show_map_markers,
1056+
marker_by=map_marker_category,
1057+
query=query,
1058+
filters=filters,
1059+
selection=selection,
1060+
map_default_span=map_default_span,
1061+
map_non_selection_alpha=map_non_selection_alpha,
1062+
map_point_size=map_point_size,
1063+
)
1064+
1065+
self._map_function = hv.DynamicMap(_map_callback, streams=[_self_stream, _table_stream])
9981066
self._station_select.source = self._map_function
9991067
self._station_select.param.watch_values(self.select_data_catalog, "index")
10001068
map_tooltip = pn.widgets.TooltipIcon(

0 commit comments

Comments
 (0)