diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1075eed3879..c3ab1609694 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,8 @@ on: env: NUMBA_NUM_THREADS: 1 + OMP_NUM_THREADS: 1 + MKL_NUM_THREADS: 1 MPLBACKEND: Agg PYTEST_ADDOPTS: --color=yes GITHUB_PR_NUMBER: ${{ github.event.number }} diff --git a/docs/changes/2549.feature.rst b/docs/changes/2549.feature.rst new file mode 100644 index 00000000000..3e045abca4b --- /dev/null +++ b/docs/changes/2549.feature.rst @@ -0,0 +1 @@ +Update bokeh dependency to version 3.x. diff --git a/environment.yml b/environment.yml index d632eb51d6e..f92fcfa6e72 100644 --- a/environment.yml +++ b/environment.yml @@ -7,7 +7,7 @@ dependencies: - pip - astropy>=5.3,<7 - black - - bokeh=2 + - bokeh=3 - nbsphinx - cython - graphviz diff --git a/pyproject.toml b/pyproject.toml index f414d8c6c3d..4ee689eace4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ requires-python = ">=3.10" dependencies = [ "astropy >=5.3,<7.0.0a0", - "bokeh ~=2.0", + "bokeh ~=3.0", "docutils", "eventio >=1.9.1, <2.0.0a0", "iminuit >=2", diff --git a/src/ctapipe/visualization/bokeh.py b/src/ctapipe/visualization/bokeh.py index 0a764ac70b9..a6300e8fd3b 100644 --- a/src/ctapipe/visualization/bokeh.py +++ b/src/ctapipe/visualization/bokeh.py @@ -21,6 +21,7 @@ ) from bokeh.palettes import Greys256, Inferno256, Magma256, Viridis256, d3 from bokeh.plotting import figure +from bokeh.transform import transform from matplotlib.colors import to_hex from ..instrument import CameraGeometry, PixelShape @@ -239,10 +240,10 @@ def norm(self, norm): else: raise ValueError(f"Unsupported norm {norm}") - self._color_mapper = norm(self.cmap) + self._color_mapper = norm(palette=self.cmap) if self._patches is not None: - color = dict(transform=self._color_mapper) - self._patches.glyph.update(fill_color=color, line_color=color) + color = transform("values", self._color_mapper) + self._patches.glyph.update(fill_color=color) if self._color_bar is not None: self._color_bar.update(color_mapper=self._color_mapper) @@ -373,7 +374,7 @@ def enable_pixel_picker(self, callback): self.figure.add_tools(TapTool()) self.datasource.selected.on_change("indices", callback) - def highlight_pixels(self, pixels, color="g", linewidth=1, alpha=0.75): + def highlight_pixels(self, pixels, color="green", linewidth=1, alpha=0.75): """ Highlight the given pixels with a colored line around them @@ -393,7 +394,7 @@ def highlight_pixels(self, pixels, color="g", linewidth=1, alpha=0.75): n_pixels = self._geometry.n_pixels pixels = np.asanyarray(pixels) - if pixels.dtype != np.bool: + if pixels.dtype != bool: selected = np.zeros(n_pixels, dtype=bool) selected[pixels] = True pixels = selected @@ -454,7 +455,6 @@ def add_ellipse(self, centroid, length, width, angle, asymmetry=0.0, **kwargs): 3rd-order moment for directionality if known kwargs: any MatPlotLib style arguments to pass to the Ellipse patch - """ ellipse = Ellipse( x=centroid[0], @@ -585,6 +585,15 @@ def __init__( frame_name = (frame or subarray.tel_coords.frame).__class__.__name__ title = f"{subarray.name} ({frame_name})" + # color by type if no value given + if values is None: + types = list({str(t) for t in subarray.telescope_types}) + cmap = cmap or d3["Category10"][10][: len(types)] + field = "type" + else: + cmap = "inferno" + field = "values" + super().__init__( use_notebook=use_notebook, title=title, @@ -595,15 +604,8 @@ def __init__( **figure_kwargs, ) - # color by type if no value given if values is None: - types = list({str(t) for t in subarray.telescope_types}) - cmap = cmap or d3["Category10"][10][: len(types)] self._color_mapper = CategoricalColorMapper(palette=cmap, factors=types) - field = "type" - else: - self.cmap = "inferno" - field = "values" self.frame = frame self.subarray = subarray @@ -617,7 +619,7 @@ def __init__( alpha=alpha, ) - color = dict(field=field, transform=self._color_mapper) + color = transform(field_name=field, transform=self._color_mapper) self._patches = self.figure.circle( x="x", y="y", @@ -687,7 +689,7 @@ def values(self, new_values): if self._patches.glyph.fill_color["field"] == "type": self.norm = "lin" self.cmap = "inferno" - color = dict(field="values", transform=self._color_mapper) + color = transform(field_name="values", transform=self._color_mapper) self._patches.glyph.update(fill_color=color, line_color=color) # recreate color bar, updating does not work here diff --git a/src/ctapipe/visualization/tests/test_bokeh.py b/src/ctapipe/visualization/tests/test_bokeh.py index 2e1dc1c8711..a6ee6906865 100644 --- a/src/ctapipe/visualization/tests/test_bokeh.py +++ b/src/ctapipe/visualization/tests/test_bokeh.py @@ -55,6 +55,8 @@ def test_camera_image(example_event, example_subarray, tmp_path): display.image = np.random.normal(size=geom.n_pixels) assert np.all(display.image == image) + display.highlight_pixels(display.image > 0) + output_path = tmp_path / "test.html" output_file(output_path) save(display.figure, filename=output_path)