diff --git a/jdaviz/configs/imviz/helper.py b/jdaviz/configs/imviz/helper.py index b10262d76f..48ce67f08d 100644 --- a/jdaviz/configs/imviz/helper.py +++ b/jdaviz/configs/imviz/helper.py @@ -2,15 +2,26 @@ import re from copy import deepcopy +from glue.core import Data + from jdaviz.core.helpers import ConfigHelper __all__ = ['Imviz'] +RESERVED_MARKER_SET_NAMES = ['all'] + class Imviz(ConfigHelper): """Imviz Helper class""" _default_configuration = 'imviz' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Markers + self._marktags = set() + self._default_mark_tag_name = 'default-marker-name' + def load_data(self, data, parser_reference=None, **kwargs): """Load data into Imviz. @@ -79,6 +90,60 @@ def load_data(self, data, parser_reference=None, **kwargs): self.app.load_data( data, parser_reference=parser_reference, **kwargs) + def _validate_marker_name(self, marker_name): + """Raise an error if the marker_name is not allowed.""" + if marker_name in RESERVED_MARKER_SET_NAMES: + raise ValueError( + f"The marker name {marker_name} is not allowed. Any name is " + f"allowed except these: {', '.join(RESERVED_MARKER_SET_NAMES)}") + + def add_markers(self, table, x_colname='x', y_colname='y', + skycoord_colname='coord', use_skycoord=False, + marker_name=None): + """Creates markers in the image at given points. + + Parameters + ---------- + table : `~astropy.table.Table` + Table containing marker locations. + + x_colname, y_colname : str + Column names for X and Y. + Coordinates must be 0-indexed. + + skycoord_colname : str + Column name with ``SkyCoord`` objects. + + use_skycoord : bool + If `True`, use ``skycoord_colname`` to mark. + Otherwise, use ``x_colname`` and ``y_colname``. + + marker_name : str, optional + Name to assign the markers in the table. Providing a name + allows markers to be removed by name at a later time. + + """ + if marker_name is None: + marker_name = self._default_mark_tag_name + + self._validate_marker_name(marker_name) + self._marktags.add(marker_name) + viewer = self.app.get_viewer("viewer-1") + + # TODO: Is this really the correct Data to use? + image = viewer.state.reference_data + + if use_skycoord: + raise NotImplementedError + else: + # TODO: Doing it this way is not compatible with blink + t_glue = Data(marker_name, **table[x_colname, y_colname]) + jglue = self.app.session.application + jglue.data_collection[marker_name] = t_glue + jglue.add_link(t_glue, x_colname, image, image.pixel_component_ids[1].label) + jglue.add_link(t_glue, y_colname, image, image.pixel_component_ids[0].label) + viewer.add_data(t_glue) + def split_filename_with_fits_ext(filename): """Split a ``filename[ext]`` input into filename and FITS extension. diff --git a/notebooks/ImvizExample.ipynb b/notebooks/ImvizExample.ipynb index 2318be6f5e..a30a02f2e0 100644 --- a/notebooks/ImvizExample.ipynb +++ b/notebooks/ImvizExample.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "7665d8ac", "metadata": {}, "source": [ "# Proof of concept of Imviz requirements using glupyter/bqplot" @@ -9,6 +10,7 @@ }, { "cell_type": "markdown", + "id": "ae582e61", "metadata": {}, "source": [ "We start off by silencing warnings that can happen when loading data as well as deprecation warnings, for clarity:" @@ -17,6 +19,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d0f7f201", "metadata": {}, "outputs": [], "source": [ @@ -27,6 +30,7 @@ { "cell_type": "code", "execution_count": null, + "id": "338fb18d", "metadata": {}, "outputs": [], "source": [ @@ -38,6 +42,7 @@ }, { "cell_type": "markdown", + "id": "85996935", "metadata": {}, "source": [ "We start off by looking at some of the basic features:" @@ -46,9 +51,8 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "id": "69eb76d7", + "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", @@ -88,6 +92,7 @@ }, { "cell_type": "markdown", + "id": "2424f16a", "metadata": {}, "source": [ "Panning and zooming is possible by showing the viewer toolbar and clicking on the '+'-shaped icon, then dragging around in the image and using scrolling to zoom in and out. To change the stretch and colormap, show the **Layer** options accessible through the last icon in the viewer toolbar.\n", @@ -98,6 +103,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e9f82d49", "metadata": {}, "outputs": [], "source": [ @@ -106,6 +112,7 @@ }, { "cell_type": "markdown", + "id": "050c05e6", "metadata": {}, "source": [ "the colormap:" @@ -114,6 +121,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f168bb80", "metadata": {}, "outputs": [], "source": [ @@ -122,6 +130,7 @@ }, { "cell_type": "markdown", + "id": "3a4f0d22", "metadata": {}, "source": [ "the limits via the percentile option:" @@ -130,14 +139,16 @@ { "cell_type": "code", "execution_count": null, + "id": "41db725d", "metadata": {}, "outputs": [], "source": [ - "viewer.state.layers[0].percentile = 99" + "viewer.state.layers[0].percentile = 95" ] }, { "cell_type": "markdown", + "id": "d6792a70", "metadata": {}, "source": [ "or the limits directly:" @@ -146,6 +157,7 @@ { "cell_type": "code", "execution_count": null, + "id": "495b0c8f", "metadata": {}, "outputs": [], "source": [ @@ -155,6 +167,7 @@ }, { "cell_type": "markdown", + "id": "63846d67", "metadata": {}, "source": [ "Note also that in the above example there are mouse-over coordinates visible by default." @@ -162,6 +175,7 @@ }, { "cell_type": "markdown", + "id": "e6b20847", "metadata": {}, "source": [ "It possible to make selections/regions in images and export these to astropy regions. Click on the viewer toolbar then click on the circular selection tool, and drag and click to select an interesting region on the sky. We can then export this region with:" @@ -170,6 +184,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d54322c5", "metadata": {}, "outputs": [], "source": [ @@ -179,6 +194,7 @@ { "cell_type": "code", "execution_count": null, + "id": "098bae4f", "metadata": {}, "outputs": [], "source": [ @@ -187,6 +203,7 @@ }, { "cell_type": "markdown", + "id": "6ea7ce76", "metadata": {}, "source": [ "Since the region is an astropy region, we can e.g. convert it to a mask:" @@ -195,6 +212,7 @@ { "cell_type": "code", "execution_count": null, + "id": "487e317c", "metadata": {}, "outputs": [], "source": [ @@ -204,6 +222,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2ffde471", "metadata": {}, "outputs": [], "source": [ @@ -213,15 +232,40 @@ { "cell_type": "code", "execution_count": null, + "id": "aa303d76", "metadata": {}, "outputs": [], "source": [ "plt.imshow(mask.to_image(data.shape), origin='lower')" ] }, + { + "cell_type": "markdown", + "id": "11fab067-7428-4ce4-bc9b-f5462fe52e2a", + "metadata": {}, + "source": [ + "It is also possible to programmatically add non-interactive markers to the image." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "270a6dd2-ce21-457f-845e-19cf5405fee7", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from astropy.table import Table\n", + "\n", + "t = Table({'x': np.random.randint(0, 500, 100),\n", + " 'y': np.random.randint(0, 500, 100)})\n", + "imviz.add_markers(t)" + ] + }, { "cell_type": "code", "execution_count": null, + "id": "f28b4a3e-6d9f-49c8-9d40-204e75a09e22", "metadata": {}, "outputs": [], "source": []