Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor get subsets to work with composite subset state #2087

Merged
merged 8 commits into from
Mar 27, 2023

Conversation

javerbukh
Copy link
Contributor

Description

This pull request is to address a few issues:

The work that still needs to happen:

  • Documentation that explains how to retrieve all combinations of data and subset information
  • Integrate into Subset Plugin (probably followup effort)
  • Find correct units to use
  • Finalize how compisite ROI subsets are returned. Currently the format is [{"name": <RegionClassName>, "glue-state": <AndState/InvertState/OrState/ROIState>, "region": <PixelRegion object>}]

Change log entry

  • Is a change log needed? If yes, is it added to CHANGES.rst? If you want to avoid merge conflicts,
    list the proposed change log here for review and add to CHANGES.rst before merge. If no, maintainer
    should add a no-changelog-entry-needed label.

Checklist for package maintainer(s)

This checklist is meant to remind the package maintainer(s) who will review this pull request of some common things to look for. This list is not exhaustive.

  • Are two approvals required? Branch protection rule does not check for the second approval. If a second approval is not necessary, please apply the trivial label.
  • Do the proposed changes actually accomplish desired goals? Also manually run the affected example notebooks, if necessary.
  • Do the proposed changes follow the STScI Style Guides?
  • Are tests added/updated as required? If so, do they follow the STScI Style Guides?
  • Are docs added/updated as required? If so, do they follow the STScI Style Guides?
  • Did the CI pass? If not, are the failures related?
  • Is a milestone set? Set this to bugfix milestone if this is a bug fix and needs to be released ASAP; otherwise, set this to the next major release milestone.
  • After merge, any internal documentations need updating (e.g., JIRA, Innerspace)?

jdaviz/app.py Outdated Show resolved Hide resolved
Copy link
Member

@kecnry kecnry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to give more thought to the actual behavior and the change in output-format between specviz and cubeviz, for example.... but just wanted to get a few thoughts down on my first pass.

This case seems incorrect to me if I'm understanding the intention of the output... without any concept of nesting, I'd expect the top two entries at the end of the video to both show as InvertState, is that right?

Screen.Recording.2023-03-16.at.5.29.24.PM.mov

jdaviz/app.py Outdated Show resolved Hide resolved
jdaviz/app.py Show resolved Hide resolved
@javerbukh
Copy link
Contributor Author

@kecnry Good find, I think I need to use AndNotState instead of InvertState since the first is more accurate but because of glue reasons that was not as easy to do. I'll give that a try now though.

@codecov
Copy link

codecov bot commented Mar 20, 2023

Codecov Report

Patch coverage: 94.04% and project coverage change: +0.03 🎉

Comparison is base (5510486) 91.90% compared to head (e21ead9) 91.93%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2087      +/-   ##
==========================================
+ Coverage   91.90%   91.93%   +0.03%     
==========================================
  Files         143      143              
  Lines       15605    15789     +184     
==========================================
+ Hits        14342    14516     +174     
- Misses       1263     1273      +10     
Impacted Files Coverage Δ
jdaviz/app.py 93.41% <91.22%> (-0.59%) ⬇️
...aviz/configs/cubeviz/plugins/tests/test_parsers.py 97.95% <100.00%> (ø)
...default/plugins/model_fitting/tests/test_plugin.py 99.52% <100.00%> (ø)
jdaviz/configs/specviz/helper.py 76.14% <100.00%> (ø)
...igs/specviz/plugins/line_analysis/line_analysis.py 97.76% <100.00%> (+0.01%) ⬆️
.../plugins/line_analysis/tests/test_line_analysis.py 99.62% <100.00%> (ø)
jdaviz/configs/specviz/tests/test_helper.py 100.00% <100.00%> (ø)

... and 4 files with indirect coverage changes

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report in Codecov by Sentry.
📢 Do you have feedback about the report comment? Let us know in this issue.

setup.cfg Outdated Show resolved Hide resolved
@pllim pllim added this to the 3.4 milestone Mar 20, 2023
jdaviz/app.py Outdated Show resolved Hide resolved
jdaviz/app.py Outdated Show resolved Hide resolved
jdaviz/app.py Outdated

dc = self.data_collection
subsets = dc.subset_groups
# TODO: Use global display units
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xref #2048

Copy link
Contributor

@pllim pllim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jesse, this PR needs a change log. Also, please grep the docs to see if any user docs need updating. Thanks!

Note to self: Ponder the implication of this PR on linked data in Imviz (e.g., usage in Aperture Photometry plugin).

@javerbukh
Copy link
Contributor Author

@pllim I thought we agreed to write changelogs after 2 approvals? I was hoping to add documentation as a follow-up to this PR
since we only have today and tomorrow with our full dev team and I would like to get this in if possible.

@pllim
Copy link
Contributor

pllim commented Mar 21, 2023

to write changelogs after 2 approvals?

You could, yes. I just did not see any mention of that plan in your post. Thanks for the clarification!

@javerbukh javerbukh force-pushed the refactor-get-subsets branch 2 times, most recently from 2048ac4 to 8b3db42 Compare March 21, 2023 18:26
@javerbukh javerbukh marked this pull request as ready for review March 21, 2023 19:23
@rosteen rosteen modified the milestones: 3.4, 3.5 Mar 22, 2023
Add get_subsets method which replaces get_subsets_from_viewer

Remove unused imports

Fix specviz helper tests

Fix failing tests and address review comments

Address review comments

Ignore ipykernel deprecation warning

Add tests covering composite spectral regions

Add check if subset label not in dc subset groups

Rename to object_only
Copy link
Member

@kecnry kecnry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to fix a few bugs and adds support for accessing nested spatial subsets. Since this is an app-level method, I think we can defer discussions/debate about API-specifics until/unless we implement a user-friendly helper-level method (I'm not sure spectral/spatial/object only options are intuitive for a user, but seem to do the trick for internal calls we need for now).

Edit: CI will need to be passing, but I'm approving now since I'll be out for a bit - if fixing CI results in significant changes, feel free to mark the approval as stale.

jdaviz/app.py Outdated Show resolved Hide resolved
@@ -246,12 +307,13 @@ def test_get_spectral_regions_unit_conversion(specviz_helper, spectrum1d):

specviz_helper.app.get_viewer("spectrum-viewer").apply_roi(XRangeROI(0.6, 0.7))

# TODO: Is this test still relevant with the upcoming glue unit conversion changes?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove this entirely? Or would re-enabling it break?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should wait until the glue unit conversion changes are implemented in jdaviz to decide what to do with this code block.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but how should it be expected to behave now? The subsets still have .lower.unit attributes, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spectral subsets, yes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test uses conv_func = uc.UnitConversion.process_unit_conversion so should it be considered relevant at all?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pllim any thoughts?

jdaviz/tests/test_subsets.py Outdated Show resolved Hide resolved
jdaviz/app.py Outdated
subsets = dc.subset_groups
# TODO: Use global display units
# units = dc[0].data.coords.spectral_axis.unit
viewer = self.get_viewer("spectrum-viewer")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is giving me error in Imviz when I have Subsets and I call imviz.app.get_subsets(). There is no doc updates to go with this, so I am not sure how I am supposed to test this in Imviz. Please advise. thanks!

File jdaviz/app.py:858, in Application.get_subsets(self, subset_name, spectral_only, spatial_only, object_only)
    855 subsets = dc.subset_groups
    856 # TODO: Use global display units
    857 # units = dc[0].data.coords.spectral_axis.unit
--> 858 viewer = self.get_viewer("spectrum-viewer")
    859 data = viewer.data()
    860 # Set axes labels for the spectrum viewer

File jdaviz/app.py:589, in Application.get_viewer(self, viewer_reference)
    564 def get_viewer(self, viewer_reference):
    565     """
    566     Return a `~glue_jupyter.bqplot.common.BqplotBaseView` viewer instance.
    567     This is *not* an ``IPyWidget``. This is stored here because
   (...)
    587         The viewer class instance.
    588     """
--> 589     return self._viewer_by_reference(viewer_reference)

File jdaviz/app.py:1409, in Application._viewer_by_reference(self, reference)
   1394 """
   1395 Viewer instance by reference defined in the yaml configuration file.
   1396 
   (...)
   1405     The viewer class instance.
   1406 """
   1407 viewer_item = self._viewer_item_by_reference(reference)
-> 1409 return self._viewer_store[viewer_item['id']]

TypeError: 'NoneType' object is not subscriptable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p.s. Didn't @bmorris3 have things like this to prevent hardcoding "spectrum-viewer"?

_default_spectrum_viewer_reference_name = "spectrum-viewer"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this was supposed to be a temporary code block until the unit conversion refactor was implemented. I have a quick fix for this (and yes, a regression test 😈 ).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix + test is in.

@@ -389,3 +389,29 @@ def test_composite_region_with_consecutive_and_not_states(cubeviz_helper):

spatial_list = cubeviz_helper.app.get_subsets("Subset 1", spatial_only=True)
assert len(spatial_list) == 3


def test_composite_region_with_imviz(imviz_helper, image_2d_wcs):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! 😸

jdaviz/app.py Outdated Show resolved Hide resolved
Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com>
@pllim
Copy link
Contributor

pllim commented Mar 23, 2023

In the Imviz example notebook, after you run the part where you add more stuff as Subsets via API, you'll get this:

Screenshot 2023-03-23 124532

Now, when you use an existing API to grab them, it will know to ignore the MaskedSubset because we don't know how to handle arbitrary masks that cannot translate to a proper regions shape yet.

>>> imviz.get_interactive_regions()
{'Subset 1': <CirclePixelRegion(center=PixCoord(x=1578.3658447265625, y=1079.412109375), radius=12.68408203125)>,
 'Subset 2': <CirclePixelRegion(center=PixCoord(x=600.0, y=400.0), radius=10.0)>,
 'Subset 3': <CirclePixelRegion(center=PixCoord(x=1379.402894779631, y=467.8100724131408), radius=20.729537026656292)>,
 'Subset 4': <CirclePixelRegion(center=PixCoord(x=600, y=200), radius=20)>,
 'Subset 5': <CirclePixelRegion(center=PixCoord(x=1379.402894779631, y=467.8100724131408), radius=41.459074053312584)>}

But with your new API, it is not filtered out.

>>> imviz.app.get_subsets()
{'Subset 1': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=1578.3658447265625, y=1079.412109375), radius=12.68408203125)>}],
 'Subset 2': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=600.0, y=400.0), radius=10.0)>}],
 'Subset 3': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=1379.402894779631, y=467.8100724131408), radius=20.729537026656292)>}],
 'Subset 4': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=600, y=200), radius=20)>}],
 'Subset 5': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=1379.402894779631, y=467.8100724131408), radius=41.459074053312584)>}],
 'MaskedSubset 1': None}

And if I start to pass non-default options to your API, it crashes:

>>> imviz.app.get_subsets(spatial_only=True, object_only=True)
jdaviz/app.py:878, in Application.get_subsets(self, subset_name, spectral_only, spatial_only, object_only)
    876 elif spatial_only and not isinstance(subset_region, SpectralRegion):
    877     if object_only:
--> 878         all_subsets[label] = [reg['region'] for reg in subset_region]
    879     else:
    880         all_subsets[label] = subset_region

TypeError: 'NoneType' object is not iterable

@pllim
Copy link
Contributor

pllim commented Mar 23, 2023

Now, if I go back to Subset1 and make it a compound subset (i.e., use the "add" and then add a Rectangle), I get this with existing API (TBH I am surprised it even work at all!):

>>> imviz.get_interactive_regions()
{'Subset 1': <CompoundPixelRegion(region1=Region: RectanglePixelRegion
 center: PixCoord(x=1564.6979764661519, y=1089.153806782053)
 width: 13.82388050415193
 height: 13.047258003918841
 angle: 0.0 rad, region2=Region: CirclePixelRegion
 center: PixCoord(x=1578.3658447265625, y=1079.412109375)
 radius: 12.68408203125, operator=<built-in function or_>)>,
...}

I think it is consistent with your API:

>>> imviz.app.get_subsets(subset_name="Subset 1")
[{'name': 'RectangularROI',
  'glue_state': 'OrState',
  'region': <RectanglePixelRegion(center=PixCoord(x=1564.6979764661519, y=1089.153806782053), width=13.82388050415193, height=13.047258003918841, angle=0.0 deg)>},
 {'name': 'CircularROI',
  'glue_state': 'RoiSubsetState',
  'region': <CirclePixelRegion(center=PixCoord(x=1578.3658447265625, y=1079.412109375), radius=12.68408203125)>}]

But again your API crashes when I pass in one more option:

>>> imviz.app.get_subsets(subset_name="Subset 1", object_only=True)
jdaviz/app.py:883, in Application.get_subsets(self, subset_name, spectral_only, spatial_only, object_only)
    881 elif not spectral_only and not spatial_only:
    882     if object_only and not isinstance(subset_region, SpectralRegion):
--> 883         all_subsets[label] = [reg['region'] for reg in subset_region]
    884     else:
    885         all_subsets[label] = subset_region

TypeError: 'NoneType' object is not iterable

@pllim
Copy link
Contributor

pllim commented Mar 23, 2023

Does not seem to affect Aperture Photometry plugin, and no Imviz tests have changed. 👏

@javerbukh
Copy link
Contributor Author

@pllim I have that case fixed now. I had not run into MaskSubsetState anywhere else so I had no idea that it needed coverage!

Copy link
Contributor

@bmorris3 bmorris3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just needs a change log entry.

@pllim
Copy link
Contributor

pllim commented Mar 24, 2023

@javerbukh , it doesn't crash but when I have masked subset, the output of imviz.app.get_subsets(spatial_only=True) and imviz.app.get_subsets(spatial_only=True, object_only=True) are inconsistent. The former still lists masked subset but not the latter. Is this expected?

@javerbukh
Copy link
Contributor Author

javerbukh commented Mar 24, 2023

@pllim

imviz.app.get_subsets(spatial_only=True)

{'Subset 1': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=2020.5693359375, y=2635.64599609375), radius=134.34112548828125)>}],
 'Subset 2': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=1714.0, y=1380.0), radius=20.0)>}],
 'Subset 3': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=2519.9463474094623, y=2682.2350667937053), radius=63.847792799004)>}],
 'Subset 4': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=1526, y=1063), radius=40)>}],
 'Subset 5': [{'name': 'CircularROI',
   'glue_state': 'RoiSubsetState',
   'region': <CirclePixelRegion(center=PixCoord(x=2519.9463474094623, y=2682.2350667937053), radius=63.847792799004)>}],
 'MaskedSubset 1': None}

and

imviz.app.get_subsets(spatial_only=True, object_only=True)

{'Subset 1': [<CirclePixelRegion(center=PixCoord(x=2020.5693359375, y=2635.64599609375), radius=134.34112548828125)>],
 'Subset 2': [<CirclePixelRegion(center=PixCoord(x=1714.0, y=1380.0), radius=20.0)>],
 'Subset 3': [<CirclePixelRegion(center=PixCoord(x=2519.9463474094623, y=2682.2350667937053), radius=63.847792799004)>],
 'Subset 4': [<CirclePixelRegion(center=PixCoord(x=1526, y=1063), radius=40)>],
 'Subset 5': [<CirclePixelRegion(center=PixCoord(x=2519.9463474094623, y=2682.2350667937053), radius=63.847792799004)>],
 'MaskedSubset 1': None}

both look fine to me. Did you do something else in your workflow? I had no idea we used MaskSubsetState until yesterday so if you would like to push some commits to get it working how you would expect, I would be totally fine with that.

@pllim
Copy link
Contributor

pllim commented Mar 24, 2023

I say, you should not even include it in your output. I don't see any point because when I do imviz.app.get_subsets(subset_name="MaskedSubset 1", object_only=True), None is returned, which is pretty useless. You can throw a NotImplementedError instead.

@javerbukh
Copy link
Contributor Author

Follow-up ticket for this work is #2114.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants