Skip to content

Commit 0d086bc

Browse files
committed
fix: Handle empty centroids in contour grid calculation to maintain array length
1 parent 9cdba93 commit 0d086bc

2 files changed

Lines changed: 44 additions & 33 deletions

File tree

dvue/animator/multi_ui.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

dvue/animator/ui.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,14 @@ def _make_contour_grid(
295295
cx_list, cy_list = [], []
296296
for geom in gdf_proj.geometry:
297297
c = geom.centroid
298-
cx_list.append(c.x)
299-
cy_list.append(c.y)
298+
if c.is_empty:
299+
# Degenerate geometry (NaN/inf coords from a bad CRS conversion).
300+
# Use NaN so the centroid array stays the same length as geo_ids.
301+
cx_list.append(float("nan"))
302+
cy_list.append(float("nan"))
303+
else:
304+
cx_list.append(c.x)
305+
cy_list.append(c.y)
300306
centroids_x = np.array(cx_list)
301307
centroids_y = np.array(cy_list)
302308

0 commit comments

Comments
 (0)