-
-
-
-
{{viewer_reference || viewer_id}}
+
+
+
+
+ {{viewer_reference || viewer_id}}
-
-
-
-
-
-
- {{layer_info.prefix_icon}}
-
- {{layer_name}}
-
+
+
+
+
+
+
+
+ mdi-chart-scatter-plot
+
+
+ mdi-chart-bell-curve
+
+
+ mdi-chart-line
+
+ {{item.label}}
+
+
+
@@ -25,19 +56,20 @@
.viewer-label {
display: block;
float: right;
- background-color: #c3c3c3c3;
- width: 24px;
+ background-color: #c3c3c32c;
+ width: 30px;
overflow: hidden;
white-space: nowrap;
- /*cursor: pointer;*/
}
.viewer-label:last-child {
- padding-bottom: 2px;
+ padding-bottom: 0px;
+ border-bottom-left-radius: 4px;
}
.viewer-label:hover {
background-color: #e5e5e5;
width: auto;
- border-bottom-left-radius: 4px;
+
border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
}
\ No newline at end of file
diff --git a/jdaviz/configs/default/plugins/plot_options/plot_options.py b/jdaviz/configs/default/plugins/plot_options/plot_options.py
index 164457d7b1..de50d5e17b 100644
--- a/jdaviz/configs/default/plugins/plot_options/plot_options.py
+++ b/jdaviz/configs/default/plugins/plot_options/plot_options.py
@@ -10,7 +10,7 @@
from traitlets import Any, Dict, Float, Bool, Int, List, Unicode, observe
from glue.core.subset_group import GroupedSubset
-from glue.config import colormaps, stretches
+from glue.config import stretches as glue_stretches
from glue.viewers.scatter.state import ScatterViewerState
from glue.viewers.profile.state import ProfileViewerState, ProfileLayerState
from glue.viewers.image.state import ImageSubsetLayerState, ImageViewerState
@@ -20,47 +20,24 @@
from glue_jupyter.common.toolbar_vuetify import read_icon
from jdaviz.core.registries import tray_registry
-from jdaviz.core.template_mixin import (PluginTemplateMixin, ViewerSelect, LayerSelect,
+from jdaviz.core.template_mixin import (PluginTemplateMixin, ViewerSelectMixin, LayerSelect,
PlotOptionsSyncState, Plot,
skip_if_no_updates_since_last_active, with_spinner)
from jdaviz.core.events import ChangeRefDataMessage
from jdaviz.core.user_api import PluginUserApi
from jdaviz.core.tools import ICON_DIR
from jdaviz.core.custom_traitlets import IntHandleEmpty
-from jdaviz.utils import is_not_wcs_only
+# by importing from utils, glue_colormaps will include the custom Random colormap
+from jdaviz.utils import is_not_wcs_only, cmap_samples, glue_colormaps
from scipy.interpolate import PchipInterpolator
-from photutils.utils import make_random_cmap
__all__ = ['PlotOptions']
RANDOM_SUBSET_SIZE = 10_000
-def _register_random_cmap(
- cmap_name,
- bkg_color=[0, 0, 0],
- bkg_alpha=1,
- seed=42,
- ncolors=10_000
-):
- """
- Custom random colormap, useful for rendering image
- segmentation maps. The default background for
- `label==0` is *transparent*. If the segmentation map
- contains more than 10,000 labels, adjust the `ncolors`
- kwarg to ensure uniqueness.
- """
- cmap = make_random_cmap(ncolors=ncolors, seed=seed)
- cmap.colors[0] = bkg_color + [bkg_alpha]
- cmap.name = cmap_name
- colormaps.add(cmap_name, cmap)
-
-
-_register_random_cmap('Random', bkg_alpha=1)
-
-
class SplineStretch:
"""
A class to represent spline stretches.
@@ -117,8 +94,8 @@ def update_knots(self, x, y):
# Add the spline stretch to the glue stretch registry if not registered
-if "spline" not in stretches:
- stretches.add("spline", SplineStretch, display="Spline")
+if "spline" not in glue_stretches:
+ glue_stretches.add("spline", SplineStretch, display="Spline")
def _round_step(step):
@@ -132,7 +109,7 @@ def _round_step(step):
@tray_registry('g-plot-options', label="Plot Options")
-class PlotOptions(PluginTemplateMixin):
+class PlotOptions(PluginTemplateMixin, ViewerSelectMixin):
"""
The Plot Options Plugin gives access to per-viewer and per-layer options and enables
setting across multiple viewers/layers simultaneously.
@@ -215,9 +192,6 @@ class PlotOptions(PluginTemplateMixin):
# read-only display units
display_units = Dict().tag(sync=True)
- viewer_multiselect = Bool(False).tag(sync=True)
- viewer_items = List().tag(sync=True)
- viewer_selected = Any().tag(sync=True) # Any needed for multiselect
viewer_limits = Dict().tag(sync=True)
layer_multiselect = Bool(False).tag(sync=True)
@@ -399,14 +373,13 @@ class PlotOptions(PluginTemplateMixin):
show_viewer_labels = Bool(True).tag(sync=True)
- cmap_samples = Dict().tag(sync=True)
+ cmap_samples = Dict(cmap_samples).tag(sync=True)
swatches_palette = List().tag(sync=True)
apply_RGB_presets_spinner = Bool(False).tag(sync=True)
stretch_hist_spinner = Bool(False).tag(sync=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.viewer = ViewerSelect(self, 'viewer_items', 'viewer_selected', 'viewer_multiselect')
self.layer = LayerSelect(self, 'layer_items', 'layer_selected',
'viewer_selected', 'layer_multiselect')
@@ -666,13 +639,6 @@ def state_attr_for_line_visible(state):
self.hub.subscribe(self, ChangeRefDataMessage,
handler=self._on_refdata_change)
- # give UI access to sampled version of the available colormap choices
- def hex_for_cmap(cmap):
- N = 50
- cm_sampled = cmap.resampled(N)
- return [matplotlib.colors.to_hex(cm_sampled(i)) for i in range(N)]
- self.cmap_samples = {cmap[1].name: hex_for_cmap(cmap[1]) for cmap in colormaps.members}
-
@property
def user_api(self):
expose = ['multiselect', 'viewer', 'viewer_multiselect', 'layer', 'layer_multiselect',
@@ -1085,7 +1051,7 @@ def _update_stretch_curve(self, msg=None):
data = stretch(data, out=data)
if color_mode == 'Colormaps':
- cmap = colormaps[self.image_colormap.text]
+ cmap = glue_colormaps[self.image_colormap.text]
if hasattr(cmap, "get_bad"):
bad_color = cmap.get_bad().tolist()[:3]
layer_cmap = cmap.with_extremes(bad=bad_color + [self.image_opacity_value])
diff --git a/jdaviz/configs/default/plugins/plot_options/plot_options.vue b/jdaviz/configs/default/plugins/plot_options/plot_options.vue
index 0eddb38884..2078a28dae 100644
--- a/jdaviz/configs/default/plugins/plot_options/plot_options.vue
+++ b/jdaviz/configs/default/plugins/plot_options/plot_options.vue
@@ -900,7 +900,6 @@ module.exports = {
}
}
style += ')'
- console.log(style)
return style
}
},
diff --git a/jdaviz/configs/imviz/tests/test_astrowidgets_api.py b/jdaviz/configs/imviz/tests/test_astrowidgets_api.py
index d41c24f3b5..aea45c16e8 100644
--- a/jdaviz/configs/imviz/tests/test_astrowidgets_api.py
+++ b/jdaviz/configs/imviz/tests/test_astrowidgets_api.py
@@ -182,9 +182,10 @@ def test_colormap_options(self):
'Gray', 'Viridis', 'Plasma', 'Inferno', 'Magma', 'Purple-Blue',
'Yellow-Green-Blue', 'Yellow-Orange-Red', 'Red-Purple', 'Blue-Green',
'Hot', 'Red-Blue', 'Red-Yellow-Blue', 'Purple-Orange', 'Purple-Green',
- 'Random', 'Rainbow', 'Seismic',
+ 'Rainbow', 'Seismic',
'Reversed: Gray', 'Reversed: Viridis', 'Reversed: Plasma', 'Reversed: Inferno',
- 'Reversed: Magma', 'Reversed: Hot', 'Reversed: Rainbow']
+ 'Reversed: Magma', 'Reversed: Hot', 'Reversed: Rainbow',
+ 'Random']
def test_invalid_colormap(self):
with pytest.raises(ValueError, match='Invalid colormap'):
diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py
index a3db290531..c97c3a8866 100644
--- a/jdaviz/core/template_mixin.py
+++ b/jdaviz/core/template_mixin.py
@@ -774,7 +774,7 @@ def __init__(self, *args, **kwargs):
def __repr__(self):
if hasattr(self, 'multiselect'):
- return f"
" # noqa
+ return f"" # noqa
return f""
def __eq__(self, other):
@@ -1531,14 +1531,18 @@ def not_spatial_subset_in_profile_viewer(lyr):
def _layer_to_dict(self, layer_label):
is_subset = None
+ subset_type = None
colors = []
visibilities = []
+ linewidths = []
for viewer in self.viewer_objs:
for layer in viewer.layers:
if layer.layer.label == layer_label and is_not_wcs_only(layer.layer):
if is_subset is None:
is_subset = ((hasattr(layer, 'state') and hasattr(layer.state, 'subset_state')) or # noqa
(hasattr(layer, 'layer') and hasattr(layer.layer, 'subset_state'))) # noqa
+ if is_subset:
+ subset_type = get_subset_type(layer.layer)
if (getattr(viewer.state, 'color_mode', None) == 'Colormaps'
and hasattr(layer.state, 'cmap')):
@@ -1548,11 +1552,14 @@ def _layer_to_dict(self, layer_label):
visibilities.append(getattr(layer.state, 'bitmap_visible', True)
and layer.visible)
+ linewidths.append(getattr(layer.state, 'linewidth', 0))
return {"label": layer_label,
"is_subset": is_subset,
+ "subset_type": subset_type,
"icon": self.app.state.layer_icons.get(layer_label),
"visible": visibilities[0] if len(list(set(visibilities))) == 1 else 'mixed',
+ "linewidth": linewidths[0] if len(list(set(linewidths))) == 1 else 'mixed',
"colors": np.unique(colors).tolist()}
def _on_viewer_selected_changed(self, msg=None):
@@ -1619,8 +1626,10 @@ def _on_data_added(self, msg=None):
if msg is None or not hasattr(msg, 'data') or msg.data is None:
return
new_data_label = msg.data.label
- viewer = self.viewer if isinstance(self.viewer, list) else [self.viewer]
- for current_viewer in viewer:
+ viewers = self.viewer if isinstance(self.viewer, list) else [self.viewer]
+ for current_viewer in viewers:
+ if not len(current_viewer):
+ continue
for layer in self._get_viewer(current_viewer).state.layers:
if layer.layer.label == new_data_label and not hasattr(layer.layer, 'subset_state'):
if is_wcs_only(layer.layer):
@@ -3304,7 +3313,7 @@ class ViewerSelectMixin(VuetifyTemplate, HubListener):
"""
viewer_items = List().tag(sync=True)
- viewer_selected = Any().tag(sync=True)
+ viewer_selected = Any().tag(sync=True) # Any needed for multiselect
viewer_multiselect = Bool(False).tag(sync=True)
def __init__(self, *args, **kwargs):
@@ -4362,7 +4371,7 @@ def _on_glue_layer_visible_changed(self, value):
self._update_mixed_state()
def _on_glue_value_changed(self, value):
- if self._glue_name == 'color_mode':
+ if self._glue_name in ('color_mode', 'linewidth'):
# then we need to force updates to the layer-icon colors
# NOTE: this will only trigger when the change to color_mode was handled
# through this plugin. Manual changes to the glue state for viewers not
diff --git a/jdaviz/utils.py b/jdaviz/utils.py
index 9d04a9a752..d15a113674 100644
--- a/jdaviz/utils.py
+++ b/jdaviz/utils.py
@@ -14,8 +14,12 @@
from astropy.units import Quantity
from astropy import units as u
from astroquery.mast import Observations, conf
+from matplotlib import colors as mpl_colors
+import matplotlib.cm as cm
+from photutils.utils import make_random_cmap
from glue.config import settings
+from glue.config import colormaps as glue_colormaps
from glue.core import BaseData
from glue.core.exceptions import IncompatibleAttribute
from glue.core.subset import SubsetState, RangeSubsetState, RoiSubsetState
@@ -30,7 +34,7 @@
'download_uri_to_path', 'flux_conversion', 'spectral_axis_conversion',
'layer_is_2d', 'layer_is_2d_or_3d', 'layer_is_image_data', 'layer_is_wcs_only',
'get_wcs_only_layer_labels', 'get_top_layer_index', 'get_reference_image_data',
- 'standardize_roman_metadata']
+ 'standardize_roman_metadata', 'cmap_samples', 'glue_colormaps']
NUMPY_LT_2_0 = not minversion("numpy", "2.0.dev")
@@ -929,3 +933,52 @@ def get_reference_image_data(app, viewer_id=None):
return refdata, iref
return None, -1
+
+
+# Add new and inverse colormaps to Glue global state. Also see ColormapRegistry in
+# https://github.com/glue-viz/glue/blob/main/glue/config.py
+new_cms = (['Rainbow', cm.rainbow],
+ ['Seismic', cm.seismic],
+ ['Reversed: Gray', cm.gray_r],
+ ['Reversed: Viridis', cm.viridis_r],
+ ['Reversed: Plasma', cm.plasma_r],
+ ['Reversed: Inferno', cm.inferno_r],
+ ['Reversed: Magma', cm.magma_r],
+ ['Reversed: Hot', cm.hot_r],
+ ['Reversed: Rainbow', cm.rainbow_r])
+for cur_cm in new_cms:
+ if cur_cm not in glue_colormaps.members:
+ glue_colormaps.add(*cur_cm)
+
+
+def _register_random_cmap(
+ cmap_name,
+ bkg_color=[0, 0, 0],
+ bkg_alpha=1,
+ seed=42,
+ ncolors=10_000
+):
+ """
+ Custom random colormap, useful for rendering image
+ segmentation maps. The default background for
+ `label==0` is *transparent*. If the segmentation map
+ contains more than 10,000 labels, adjust the `ncolors`
+ kwarg to ensure uniqueness.
+ """
+ cmap = make_random_cmap(ncolors=ncolors, seed=seed)
+ cmap.colors[0] = bkg_color + [bkg_alpha]
+ cmap.name = cmap_name
+ glue_colormaps.add(cmap_name, cmap)
+
+
+_register_random_cmap('Random', bkg_alpha=1)
+
+
+# give UI access to sampled version of the available colormap choices
+def _hex_for_cmap(cmap):
+ N = 50
+ cm_sampled = cmap.resampled(N)
+ return [mpl_colors.to_hex(cm_sampled(i)) for i in range(N)]
+
+
+cmap_samples = {cmap[1].name: _hex_for_cmap(cmap[1]) for cmap in glue_colormaps.members}