@@ -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