diff --git a/CHANGES.rst b/CHANGES.rst index 855e979f7a..18c2000a89 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,8 @@ Bug Fixes - Prevent duplicate labels by changing duplicate number appended to label to max number (of duplicates) plus 1. [#1824] +- Layer lettering now supports up to 702 layers. Beyond that, special characters are used. [#1850] + Cubeviz ^^^^^^^ diff --git a/jdaviz/app.py b/jdaviz/app.py index 889baf9aec..602a86a761 100644 --- a/jdaviz/app.py +++ b/jdaviz/app.py @@ -47,7 +47,7 @@ from jdaviz.core.registries import (tool_registry, tray_registry, viewer_registry, data_parser_registry) from jdaviz.core.tools import ICON_DIR -from jdaviz.utils import SnackbarQueue, ColorCycler +from jdaviz.utils import SnackbarQueue, ColorCycler, alpha_index from ipypopout import PopoutButton __all__ = ['Application'] @@ -380,7 +380,7 @@ def _on_layers_changed(self, msg): if layer_name not in self.state.layer_icons: self.state.layer_icons = {**self.state.layer_icons, - layer_name: chr(97 + len(self.state.layer_icons))} + layer_name: alpha_index(len(self.state.layer_icons))} def _link_new_data(self, reference_data=None, data_to_be_linked=None): """ diff --git a/jdaviz/tests/test_utils.py b/jdaviz/tests/test_utils.py new file mode 100644 index 0000000000..c243c1a714 --- /dev/null +++ b/jdaviz/tests/test_utils.py @@ -0,0 +1,16 @@ +import pytest + +from jdaviz import utils + + +@pytest.mark.parametrize("test_input,expected", [(0, 'a'), (1, 'b'), (25, 'z'), (26, 'aa'), + (701, 'zz'), (702, '{a')]) +def test_alpha_index(test_input, expected): + assert utils.alpha_index(test_input) == expected + + +def test_alpha_index_exceptions(): + with pytest.raises(TypeError, match="index must be an integer"): + utils.alpha_index(4.2) + with pytest.raises(ValueError, match="index must be positive"): + utils.alpha_index(-1) diff --git a/jdaviz/utils.py b/jdaviz/utils.py index a8488c5c58..3fa4890d52 100644 --- a/jdaviz/utils.py +++ b/jdaviz/utils.py @@ -10,7 +10,7 @@ from glue.config import settings __all__ = ['SnackbarQueue', 'enable_hot_reloading', 'bqplot_clear_figure', - 'standardize_metadata', 'ColorCycler'] + 'standardize_metadata', 'ColorCycler', 'alpha_index'] # For Metadata Viewer plugin internal use only. PRIHDR_KEY = '_primary_header' @@ -175,6 +175,42 @@ def bqplot_clear_figure(fig): setattr(fig, 'axis_registry', {}) +def alpha_index(index): + """Converts an index to label (A-Z, AA-ZZ). + + Parameters + ---------- + index : int + Index between 0 and 701, inclusive. Higher number is accepted but + will have special characters. + + Returns + ------- + label : str + String in the range A-Z, AA-ZZ if index is within 0-701 range, inclusive. + + Raises + ------ + TypeError + Index is not integer. + + ValueError + Index is negative. + """ + # if we ever want to support more than 702 layers, then we'll need a third + # "digit" and will need to account for the horizontal space in the legends + if not isinstance(index, int): + raise TypeError("index must be an integer") + if index < 0: + raise ValueError("index must be positive") + if index <= 25: + # a-z + return chr(97 + index) + else: + # aa-zz (26-701), then overflow strings like '{a' + return chr(97 + index//26 - 1) + chr(97 + index % 26) + + def standardize_metadata(metadata): """Standardize given metadata so it can be viewed in Metadata Viewer plugin. The input can be plain