diff --git a/CHANGES.rst b/CHANGES.rst
index c2e5160645..53a640696e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -126,7 +126,8 @@ Cubeviz
Imviz
^^^^^
-- Fixes cropped image layer with WCS-linking without fast-approximation. [#1908]
+- Fixes cropped image layer with WCS-linking without fast-approximation, mouseover display
+ now shows when information is outside original reference data bounding box. [#1908]
Mosviz
^^^^^^
diff --git a/docs/imviz/displayimages.rst b/docs/imviz/displayimages.rst
index 551e17b293..b1b191e51f 100644
--- a/docs/imviz/displayimages.rst
+++ b/docs/imviz/displayimages.rst
@@ -35,6 +35,10 @@ cursor's location in pixel space (X and Y), the RA and Dec at that point, and th
of the data there. This information is displayed in the top bar of the UI, on the
middle-right side.
+If the mouse is outside the original bounding-box of the reference data, the transformation
+from pixels to sky coordinates is less reliable. This is indicated by "(est.)" and the sky
+coordinates becoming gray.
+
Home
====
diff --git a/jdaviz/configs/default/plugins/subset_plugin/subset_plugin.py b/jdaviz/configs/default/plugins/subset_plugin/subset_plugin.py
index a0984ab71d..76f8e91dff 100644
--- a/jdaviz/configs/default/plugins/subset_plugin/subset_plugin.py
+++ b/jdaviz/configs/default/plugins/subset_plugin/subset_plugin.py
@@ -249,7 +249,7 @@ def vue_recenter_subset(self, *args):
# the reference data. However, Subset is always defined w.r.t.
# the reference data, so we need to convert back.
viewer = self.app._jdaviz_helper.default_viewer
- x, y, _ = viewer._get_real_xy(
+ x, y, _, _ = viewer._get_real_xy(
data, phot_aperstats.xcentroid, phot_aperstats.ycentroid, reverse=True)
if not np.all(np.isfinite((x, y))):
raise ValueError(f'Invalid centroid ({x}, {y})')
diff --git a/jdaviz/configs/imviz/plugins/coords_info/coords_info.py b/jdaviz/configs/imviz/plugins/coords_info/coords_info.py
index e34046c11a..a621a771e3 100644
--- a/jdaviz/configs/imviz/plugins/coords_info/coords_info.py
+++ b/jdaviz/configs/imviz/plugins/coords_info/coords_info.py
@@ -1,4 +1,4 @@
-from traitlets import Unicode
+from traitlets import Bool, Unicode
from jdaviz.core.registries import tool_registry
from jdaviz.core.template_mixin import TemplateMixin
@@ -19,6 +19,7 @@ class CoordsInfo(TemplateMixin):
world_dec = Unicode("").tag(sync=True)
world_ra_deg = Unicode("").tag(sync=True)
world_dec_deg = Unicode("").tag(sync=True)
+ within_bounding_box = Bool(True).tag(sync=True)
def reset_coords_display(self):
self.world_label_prefix = '\u00A0'
@@ -29,7 +30,7 @@ def reset_coords_display(self):
self.world_ra_deg = ''
self.world_dec_deg = ''
- def set_coords(self, sky):
+ def set_coords(self, sky, within_bounding_box=True):
celestial_coordinates = sky.to_string('hmsdms', precision=4, pad=True).split()
celestial_coordinates_deg = sky.to_string('decimal', precision=10, pad=True).split()
world_ra = celestial_coordinates[0]
@@ -47,3 +48,4 @@ def set_coords(self, sky):
self.world_dec = world_dec
self.world_ra_deg = world_ra_deg
self.world_dec_deg = world_dec_deg
+ self.within_bounding_box = within_bounding_box
diff --git a/jdaviz/configs/imviz/plugins/coords_info/coords_info.vue b/jdaviz/configs/imviz/plugins/coords_info/coords_info.vue
index 7fc28fe3f4..4bb19cd84c 100644
--- a/jdaviz/configs/imviz/plugins/coords_info/coords_info.vue
+++ b/jdaviz/configs/imviz/plugins/coords_info/coords_info.vue
@@ -10,14 +10,14 @@
Pixel {{ pixel }} Value {{ value }}
-
+
{{ world_label_prefix }} |
{{ world_ra }} |
{{ world_dec }} |
{{ world_label_icrs }} |
-
- |
+
+ {{ within_bounding_box ? '' : '(est.)' }} |
{{ world_ra_deg }} |
{{ world_dec_deg }} |
{{ world_label_deg }} |
diff --git a/jdaviz/configs/imviz/plugins/parsers.py b/jdaviz/configs/imviz/plugins/parsers.py
index 3e3782e54e..76f862aa26 100644
--- a/jdaviz/configs/imviz/plugins/parsers.py
+++ b/jdaviz/configs/imviz/plugins/parsers.py
@@ -125,6 +125,10 @@ def _parse_image(app, file_obj, data_label, ext=None):
for data, data_label in data_iter:
if data.coords is not None:
+ # keep a copy of the original bounding box so we can detect
+ # when extrapolating beyond, but then remove the bounding box
+ # so that image layers are not cropped.
+ data.coords._orig_bounding_box = data.coords.bounding_box
data.coords.bounding_box = None
data_label = app.return_data_label(data_label, alt_name="image_data")
app.add_data(data, data_label)
diff --git a/jdaviz/configs/imviz/plugins/tools.py b/jdaviz/configs/imviz/plugins/tools.py
index fc6bb9c2cb..5f4b28cbb8 100644
--- a/jdaviz/configs/imviz/plugins/tools.py
+++ b/jdaviz/configs/imviz/plugins/tools.py
@@ -101,7 +101,7 @@ def on_click(self, data):
y = data['domain']['y']
if x is None or y is None: # Out of bounds
return
- x, y, _ = self.viewer._get_real_xy(image, x, y)
+ x, y, _, _ = self.viewer._get_real_xy(image, x, y)
self.viewer.center_on((x, y))
diff --git a/jdaviz/configs/imviz/plugins/viewers.py b/jdaviz/configs/imviz/plugins/viewers.py
index caaca890de..05c74e2457 100644
--- a/jdaviz/configs/imviz/plugins/viewers.py
+++ b/jdaviz/configs/imviz/plugins/viewers.py
@@ -106,7 +106,7 @@ def on_mouse_or_key_event(self, data):
maxsize = int(np.ceil(np.log10(np.max(image.shape)))) + 3
fmt = 'x={0:0' + str(maxsize) + '.1f} y={1:0' + str(maxsize) + '.1f}'
- x, y, coords_status = self._get_real_xy(image, x, y)
+ x, y, coords_status, within_bounding_box = self._get_real_xy(image, x, y)
self.label_mouseover.pixel = (fmt.format(x, y))
if coords_status:
@@ -115,7 +115,7 @@ def on_mouse_or_key_event(self, data):
except Exception: # WCS might not be celestial
self.label_mouseover.reset_coords_display()
else:
- self.label_mouseover.set_coords(coo)
+ self.label_mouseover.set_coords(coo, within_bounding_box=within_bounding_box)
else:
self.label_mouseover.reset_coords_display()
@@ -155,7 +155,7 @@ def on_mouse_or_key_event(self, data):
y = data['domain']['y']
if x is None or y is None: # Out of bounds
return
- x, y, _ = self._get_real_xy(image, x, y)
+ x, y, _, _ = self._get_real_xy(image, x, y)
self.line_profile_xy.selected_x = x
self.line_profile_xy.selected_y = y
self.line_profile_xy.selected_viewer = self.reference_id
@@ -221,7 +221,9 @@ def on_limits_change(self, *args):
self.set_compass(self.state.layers[i].layer)
def _get_real_xy(self, image, x, y, reverse=False):
- """Return real (X, Y) position and status in case of dithering.
+ """Return real (X, Y) position and status in case of dithering as well as whether the
+ results were within the bounding box of the reference data or required possibly inaccurate
+ extrapolation.
``coords_status`` is for ``self.label_mouseover`` coords handling only.
When `True`, it sets the coords, otherwise it resets.
@@ -234,10 +236,16 @@ def _get_real_xy(self, image, x, y, reverse=False):
# Convert these to a SkyCoord via WCS - note that for other datasets
# we aren't actually guaranteed to get a SkyCoord out, just for images
# with valid celestial WCS
+ within_bounding_box = True
try:
# Convert X,Y from reference data to the one we are actually seeing.
# world_to_pixel return scalar ndarray that we need to convert to float.
if self.get_link_type(image.label) == 'wcs':
+ bb = self.state.reference_data.coords._orig_bounding_box
+ if bb is not None:
+ ints = bb.intervals
+ within_bounding_box = (ints[0].lower <= x <= ints[0].upper and
+ ints[1].lower <= y <= ints[1].upper)
if not reverse:
x, y = list(map(float, image.coords.world_to_pixel(
self.state.reference_data.coords.pixel_to_world(x, y))))
@@ -250,7 +258,7 @@ def _get_real_xy(self, image, x, y, reverse=False):
else:
coords_status = False
- return x, y, coords_status
+ return x, y, coords_status, within_bounding_box
def _get_zoom_limits(self, image):
"""Return a list of ``(x, y)`` that defines four corners of
diff --git a/jdaviz/core/region_translators.py b/jdaviz/core/region_translators.py
index a3c755c177..17e6d3defe 100644
--- a/jdaviz/core/region_translators.py
+++ b/jdaviz/core/region_translators.py
@@ -65,7 +65,7 @@ def _get_region_from_spatial_subset(plugin_obj, subset_label):
# not loaded in all the viewers, so use default viewer.
viewer = plugin_obj.app._jdaviz_helper.default_viewer
- x, y, _ = viewer._get_real_xy(
+ x, y, _, _ = viewer._get_real_xy(
plugin_obj.app.data_collection[plugin_obj.dataset_selected],
reg.center.x, reg.center.y)
reg.center.x = x