Skip to content

Commit

Permalink
subset tools import_region to use batch_load (#3321)
Browse files Browse the repository at this point in the history
* subset plugin delay loading plugin results into viewers
  • Loading branch information
kecnry authored Dec 5, 2024
1 parent 2566a82 commit 9ab6607
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 104 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ New Features

* New design for viewer legend and data-menu. [#3220, #3254, #3263, #3264, #3271, #3272, #3274, #3289, #3310]

* Improve performance while importing multiple regions. [#3321]

Cubeviz
^^^^^^^

Expand Down
192 changes: 97 additions & 95 deletions jdaviz/configs/default/plugins/subset_tools/subset_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,109 +882,111 @@ def _load_regions(self, regions, combination_mode=None, max_num_regions=None,

previous_mode = self.app.session.edit_subset_mode.mode

for index, region in enumerate(regions):
# Set combination mode for how region will be applied to current subset
# or created as a new subset
if combo_mode_is_list:
combo_mode = combination_mode[index]
else:
combo_mode = combination_mode

if combo_mode == 'new':
# Remove selection of subset so that new one will be created
self.app.session.edit_subset_mode.edit_subset = None # No overwrite next iteration
self.app.session.edit_subset_mode.mode = SUBSET_MODES_PRETTY['new']
elif combo_mode:
self.combination_mode.selected = combo_mode

if (isinstance(region, (SkyCircularAperture, SkyEllipticalAperture,
SkyRectangularAperture, SkyCircularAnnulus,
CircleSkyRegion, EllipseSkyRegion,
RectangleSkyRegion, CircleAnnulusSkyRegion))
and not has_wcs):
bad_regions.append((region, 'Sky region provided but data has no valid WCS'))
continue

if (isinstance(region, (CircularAperture, EllipticalAperture,
RectangularAperture, CircularAnnulus,
CirclePixelRegion, EllipsePixelRegion,
RectanglePixelRegion, CircleAnnulusPixelRegion))
and (hasattr(self.app, '_link_type') and self.app._link_type == "wcs")):
bad_regions.append((region, 'Pixel region provided by data is linked by WCS'))
continue

# photutils: Convert to region shape first
if isinstance(region, (CircularAperture, SkyCircularAperture,
EllipticalAperture, SkyEllipticalAperture,
RectangularAperture, SkyRectangularAperture,
CircularAnnulus, SkyCircularAnnulus)):
region = aperture2regions(region)

# region: Convert to ROI.
# NOTE: Out-of-bounds ROI will succeed; this is native glue behavior.
if isinstance(region, (CirclePixelRegion, CircleSkyRegion,
EllipsePixelRegion, EllipseSkyRegion,
RectanglePixelRegion, RectangleSkyRegion,
CircleAnnulusPixelRegion, CircleAnnulusSkyRegion)):
state = regions2roi(region, wcs=data.coords)
viewer.apply_roi(state)

elif isinstance(region, (CircularROI, CircularAnnulusROI,
EllipticalROI, RectangularROI)):
viewer.apply_roi(region)

elif isinstance(region, SpectralRegion):
# Use viewer_name if provided in kwarg, otherwise use
# default spectrum viewer name
viewer_name = (viewer_parameter or
self.app._jdaviz_helper._default_spectrum_viewer_reference_name)
range_viewer = self.app.get_viewer(viewer_name)

s = RangeSubsetState(lo=region.lower.value, hi=region.upper.value,
att=range_viewer.state.x_att)
range_viewer.apply_subset_state(s)

# Last resort: Masked Subset that is static (if data is not a cube)
elif data.ndim == 2:
im = None
if hasattr(region, 'to_pixel'): # Sky region: Convert to pixel region
if not has_wcs:
bad_region.append((region, 'Sky region provided but data has no valid WCS')) # noqa
with self.app._jdaviz_helper.batch_load():
for index, region in enumerate(regions):
# Set combination mode for how region will be applied to current subset
# or created as a new subset
if combo_mode_is_list:
combo_mode = combination_mode[index]
else:
combo_mode = combination_mode

if combo_mode == 'new':
# Remove selection of subset so that new one will be created
# No overwrite next iteration
self.app.session.edit_subset_mode.edit_subset = None
self.app.session.edit_subset_mode.mode = SUBSET_MODES_PRETTY['new']
elif combo_mode:
self.combination_mode.selected = combo_mode

if (isinstance(region, (SkyCircularAperture, SkyEllipticalAperture,
SkyRectangularAperture, SkyCircularAnnulus,
CircleSkyRegion, EllipseSkyRegion,
RectangleSkyRegion, CircleAnnulusSkyRegion))
and not has_wcs):
bad_regions.append((region, 'Sky region provided but data has no valid WCS'))
continue

if (isinstance(region, (CircularAperture, EllipticalAperture,
RectangularAperture, CircularAnnulus,
CirclePixelRegion, EllipsePixelRegion,
RectanglePixelRegion, CircleAnnulusPixelRegion))
and (hasattr(self.app, '_link_type') and self.app._link_type == "wcs")):
bad_regions.append((region, 'Pixel region provided by data is linked by WCS'))
continue

# photutils: Convert to region shape first
if isinstance(region, (CircularAperture, SkyCircularAperture,
EllipticalAperture, SkyEllipticalAperture,
RectangularAperture, SkyRectangularAperture,
CircularAnnulus, SkyCircularAnnulus)):
region = aperture2regions(region)

# region: Convert to ROI.
# NOTE: Out-of-bounds ROI will succeed; this is native glue behavior.
if isinstance(region, (CirclePixelRegion, CircleSkyRegion,
EllipsePixelRegion, EllipseSkyRegion,
RectanglePixelRegion, RectangleSkyRegion,
CircleAnnulusPixelRegion, CircleAnnulusSkyRegion)):
state = regions2roi(region, wcs=data.coords)
viewer.apply_roi(state)

elif isinstance(region, (CircularROI, CircularAnnulusROI,
EllipticalROI, RectangularROI)):
viewer.apply_roi(region)

elif isinstance(region, SpectralRegion):
# Use viewer_name if provided in kwarg, otherwise use
# default spectrum viewer name
viewer_name = (viewer_parameter or
self.app._jdaviz_helper._default_spectrum_viewer_reference_name)
range_viewer = self.app.get_viewer(viewer_name)

s = RangeSubsetState(lo=region.lower.value, hi=region.upper.value,
att=range_viewer.state.x_att)
range_viewer.apply_subset_state(s)

# Last resort: Masked Subset that is static (if data is not a cube)
elif data.ndim == 2:
im = None
if hasattr(region, 'to_pixel'): # Sky region: Convert to pixel region
if not has_wcs:
bad_regions.append((region, 'Sky region provided but data has no valid WCS')) # noqa
continue
region = region.to_pixel(data.coords)

if hasattr(region, 'to_mask'):
try:
mask = region.to_mask(**kwargs)
im = mask.to_image(data.shape) # Can be None
except Exception as e: # pragma: no cover
bad_regions.append((region, f'Failed to load: {repr(e)}'))
continue

# Boolean mask as input is supported but not advertised.
elif (isinstance(region, np.ndarray) and region.shape == data.shape
and region.dtype == np.bool_):
im = region

if im is None:
bad_regions.append((region, 'Mask creation failed'))
continue
region = region.to_pixel(data.coords)

if hasattr(region, 'to_mask'):
# NOTE: Region creation info is thus lost.
try:
mask = region.to_mask(**kwargs)
im = mask.to_image(data.shape) # Can be None
subset_label = f'{msg_prefix} {msg_count}'
state = MaskSubsetState(im, data.pixel_component_ids)
self.app.data_collection.new_subset_group(subset_label, state)
msg_count += 1
except Exception as e: # pragma: no cover
bad_regions.append((region, f'Failed to load: {repr(e)}'))
continue

# Boolean mask as input is supported but not advertised.
elif (isinstance(region, np.ndarray) and region.shape == data.shape
and region.dtype == np.bool_):
im = region

if im is None:
else:
bad_regions.append((region, 'Mask creation failed'))
continue

# NOTE: Region creation info is thus lost.
try:
subset_label = f'{msg_prefix} {msg_count}'
state = MaskSubsetState(im, data.pixel_component_ids)
self.app.data_collection.new_subset_group(subset_label, state)
msg_count += 1
except Exception as e: # pragma: no cover
bad_regions.append((region, f'Failed to load: {repr(e)}'))
continue
else:
bad_regions.append((region, 'Mask creation failed'))
continue
n_loaded += 1
if max_num_regions is not None and n_loaded >= max_num_regions:
break
n_loaded += 1
if max_num_regions is not None and n_loaded >= max_num_regions:
break

# Revert edit mode to before the import_region call
self.app.session.edit_subset_mode.mode = previous_mode
Expand Down
24 changes: 15 additions & 9 deletions jdaviz/core/template_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4063,15 +4063,21 @@ def add_results_from_plugin(self, data_item, replace=None, label=None):
else:
this_replace = isinstance(this_viewer, BqplotImageView)

self.app.add_data_to_viewer(viewer_ref,
label,
visible=visible, clear_other_data=this_replace)

if preserved != {}:
layer_state = [layer.state for layer in this_viewer.layers if
layer.layer.label == label][0]
for att in preserved:
setattr(layer_state, att, preserved[att])
if self.app._jdaviz_helper._in_batch_load:
# NOTE: this currently only stores the viewer reference, and so
# will not handle preserving layer options if overwriting an existing
# entry.
self.app._jdaviz_helper._delayed_show_in_viewer_labels[label] = viewer_ref
else:
self.app.add_data_to_viewer(viewer_ref,
label,
visible=visible, clear_other_data=this_replace)

if preserved != {}:
layer_state = [layer.state for layer in this_viewer.layers if
layer.layer.label == label][0]
for att in preserved:
setattr(layer_state, att, preserved[att])

# update overwrite warnings, etc
self._on_label_changed()
Expand Down

0 comments on commit 9ab6607

Please sign in to comment.