@@ -284,7 +284,6 @@ class MultiGeoAnimatorManager(pn.viewable.Viewer):
284284 vmin : Optional [float ] = param .Number (default = None , allow_None = True )
285285 vmax : Optional [float ] = param .Number (default = None , allow_None = True )
286286 colormap : str = param .Selector (default = "rainbow" , objects = CURATED_COLORMAPS )
287- diff_colormap : str = param .Selector (default = "coolwarm" , objects = CURATED_COLORMAPS )
288287 size : float = param .Number (default = 6.0 , bounds = (1.0 , 50.0 ))
289288 show_diff : bool = param .Boolean (
290289 default = False , doc = "Show A−B diff map instead of side-by-side."
@@ -307,7 +306,6 @@ def __init__(
307306 title_b : str = "Study B" ,
308307 geo_id_column : str = "geo_id" ,
309308 colormap : str = "rainbow" ,
310- diff_colormap : str = "coolwarm" ,
311309 vmin = None ,
312310 vmax = None ,
313311 size : float = 6.0 ,
@@ -421,7 +419,7 @@ def __init__(
421419 md = _build_bokeh_map (
422420 self ._gdf_a_proj , self ._geo_ids_a , bk_xs_a , bk_ys_a ,
423421 self ._geom_type_a , [np .nan ] * len (self ._geo_ids_a ),
424- diff_colormap , - 1.0 , 1.0 , size , map_height ,
422+ colormap , eff_vmin , eff_vmax , size , map_height ,
425423 f"{ title_a } \u2212 { title_b } \u2014 { ts0 } " , geo_id_column ,
426424 shared_x_range = shared_x , shared_y_range = shared_y ,
427425 )
@@ -504,8 +502,13 @@ def __init__(
504502 sizing_mode = "stretch_width" ,
505503 )
506504 self ._diff_colormap_select = pn .widgets .Select (
507- name = "Diff colormap" , options = CURATED_COLORMAPS_WITH_SEP , value = diff_colormap ,
508- sizing_mode = "stretch_width" , visible = show_diff ,
505+ name = "Diff colormap" , options = CURATED_COLORMAPS_WITH_SEP , value = "coolwarm" ,
506+ sizing_mode = "stretch_width" , visible = False ,
507+ )
508+ self ._size_slider = pn .widgets .FloatSlider (
509+ name = "Line width" ,
510+ start = 1.0 , end = 50.0 , step = 0.5 , value = size ,
511+ sizing_mode = "stretch_width" ,
509512 )
510513 self ._show_diff_check = pn .widgets .Checkbox (
511514 name = "Show diff (A \u2212 B)" , value = show_diff ,
@@ -577,14 +580,14 @@ def __init__(
577580 _appearance_card = pn .Card (
578581 self ._clim_input ,
579582 self ._colormap_select ,
583+ self ._size_slider ,
580584 self ._channels_alpha_slider ,
581585 self ._basemap_alpha_slider ,
582586 title = "Appearance" , collapsed = False ,
583587 sizing_mode = "stretch_width" ,
584588 )
585589 _diff_card = pn .Card (
586590 self ._show_diff_check ,
587- self ._diff_colormap_select ,
588591 title = "Diff (A − B)" , collapsed = False ,
589592 sizing_mode = "stretch_width" ,
590593 )
@@ -660,7 +663,7 @@ def __init__(
660663 # ----------------------------------------------------------------
661664 super ().__init__ (
662665 vmin = vmin , vmax = vmax ,
663- colormap = colormap , diff_colormap = diff_colormap ,
666+ colormap = colormap ,
664667 size = size , show_diff = show_diff ,
665668 ** params ,
666669 )
@@ -671,10 +674,9 @@ def __init__(
671674 self ._time_slider .param .watch (self ._on_slider_change , "value" )
672675 self ._datetime_picker .param .watch (self ._on_datetime_picker_change , "value" )
673676 self .param .watch (self ._on_style_change , ["vmin" , "vmax" , "colormap" , "size" ])
674- self .param .watch (self ._on_diff_colormap_change , ["diff_colormap" ])
675677 self ._clim_input .param .watch (self ._on_clim_text_change , "value" )
676678 self ._colormap_select .param .watch (self ._on_colormap_change , "value" )
677- self ._diff_colormap_select .param .watch (self ._on_diff_colormap_widget_change , "value" )
679+ self ._size_slider .param .watch (self ._on_size_widget_change , "value" )
678680 self ._show_diff_check .param .watch (self ._on_diff_toggle , "value" )
679681 self ._contours_check .param .watch (self ._on_contours_toggle , "value" )
680682 self ._contour_color_check .param .watch (self ._on_contour_color_toggle , "value" )
@@ -736,7 +738,6 @@ def collect_state(self) -> dict:
736738 },
737739 "diff" : {
738740 "show" : self .show_diff ,
739- "colormap" : self .diff_colormap ,
740741 },
741742 "x2" : {"enabled" : False , "threshold" : 2700.0 },
742743 }
@@ -923,22 +924,19 @@ def _update_diff_map(self, ts: pd.Timestamp) -> None:
923924 "xs" : self ._src_a .data ["xs" ],
924925 "ys" : self ._src_a .data ["ys" ],
925926 "_value" : vals ,
927+ "_color_value" : vals ,
926928 "geo_id" : self ._geo_ids_a ,
927929 }
928930 ts_str = ts .strftime ("%Y-%m-%d %H:%M" )
929931 self ._fig_diff .title .text = (
930932 f"{ self ._title_a } \u2212 { self ._title_b } \u2014 { ts_str } "
931933 )
932- d = self ._diff_reader_cache
933- if d is not None :
934- absmax = max (abs (d .vmin ), abs (d .vmax ), 1e-9 )
935- self ._mapper_diff .low = - absmax
936- self ._mapper_diff .high = absmax
934+ eff_vmin , eff_vmax = self ._current_clim ()
935+ self ._mapper_diff .low = eff_vmin
936+ self ._mapper_diff .high = eff_vmax
937937 if self ._ctour_diff .renderer .visible :
938- absmax = max (
939- abs (self ._mapper_diff .low ), abs (self ._mapper_diff .high ), 1e-9 )
940938 self ._recompute_contours (
941- self ._ctour_diff , vals , - absmax , absmax , self .diff_colormap )
939+ self ._ctour_diff , vals , eff_vmin , eff_vmax , self .colormap )
942940
943941 def _apply_frame (self , idx : int , ts_str : str ) -> None :
944942 """All Bokeh mutations for one frame step — must run under document lock."""
@@ -1002,17 +1000,35 @@ def _on_style_change(self, *events) -> None:
10021000 else :
10031001 self ._apply_bokeh_style ()
10041002
1003+ def _on_size_widget_change (self , event : param .parameterized .Event ) -> None :
1004+ self .size = float (event .new )
1005+
10051006 def _apply_bokeh_style (self ) -> None :
10061007 eff_vmin , eff_vmax = self ._current_clim ()
10071008 from bokeh .models import BasicTicker , BasicTickFormatter
10081009 pal = _cmap_to_palette (self .colormap )
1009- for mapper in (self ._mapper_a , self ._mapper_b ):
1010+ for mapper in (self ._mapper_a , self ._mapper_b , self . _mapper_diff ):
10101011 mapper .palette = pal
10111012 mapper .low = eff_vmin
10121013 mapper .high = eff_vmax
10131014 for cb in (self ._colorbar_a , self ._colorbar_b ):
10141015 cb .ticker = BasicTicker (desired_num_ticks = 6 )
10151016 cb .formatter = BasicTickFormatter ()
1017+ # Apply line width to all three figures' data renderers.
1018+ new_size = float (self .size )
1019+ _skip = {
1020+ id (self ._ctour_a .renderer ), id (self ._ctour_b .renderer ),
1021+ id (self ._ctour_diff .renderer ),
1022+ }
1023+ for fig in (self ._fig_a , self ._fig_b , self ._fig_diff ):
1024+ for r in fig .renderers :
1025+ if id (r ) in _skip or not hasattr (r , "glyph" ):
1026+ continue
1027+ g = r .glyph
1028+ if hasattr (g , "size" ):
1029+ g .size = new_size
1030+ elif hasattr (g , "line_width" ) and not hasattr (g , "fill_color" ):
1031+ g .line_width = new_size
10161032 # If contours are visible, refresh them (colours may have changed).
10171033 if self ._ctour_a .renderer .visible :
10181034 idx = self ._time_slider .value
@@ -1021,22 +1037,12 @@ def _apply_bokeh_style(self) -> None:
10211037 self ._update_map_b (idx , ts )
10221038
10231039 def _on_diff_colormap_change (self , * events ) -> None :
1024- pal = _cmap_to_palette (self .diff_colormap )
1025- doc = self ._active_doc ()
1026- if doc is not None :
1027- doc .add_next_tick_callback (
1028- lambda : setattr (self ._mapper_diff , "palette" , pal ))
1029- else :
1030- self ._mapper_diff .palette = pal
1040+ pass # diff mapper driven by Appearance colormap via _apply_bokeh_style
10311041
10321042 def _on_colormap_change (self , event : param .parameterized .Event ) -> None :
10331043 if event .new in CURATED_COLORMAPS : # ignore separator clicks
10341044 self .colormap = event .new
10351045
1036- def _on_diff_colormap_widget_change (self , event : param .parameterized .Event ) -> None :
1037- if event .new in CURATED_COLORMAPS : # ignore separator clicks
1038- self .diff_colormap = event .new
1039-
10401046 def _on_clim_text_change (self , event : param .parameterized .Event ) -> None :
10411047 try :
10421048 parts = [float (p .strip ()) for p in event .new .split ("," ) if p .strip ()]
@@ -1089,7 +1095,6 @@ def _apply():
10891095
10901096 def _on_diff_toggle (self , event : param .parameterized .Event ) -> None :
10911097 self .show_diff = bool (event .new )
1092- self ._diff_colormap_select .visible = self .show_diff
10931098 if self .show_diff :
10941099 self ._maps_pane .objects = [self ._pane_diff ]
10951100 idx = self ._time_slider .value
0 commit comments