From 89cffa5a2bcab753ebce61889b38322c2c802740 Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:09:07 -0500 Subject: [PATCH] Concept notebook for composite subset for spatial subsets Add specviz pondering, update imviz --- .gitignore | 1 + .../concepts/imviz_composite_subset_api.ipynb | 214 +++++++++++++++ .../specviz_composite_subset_api.ipynb | 258 ++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 notebooks/concepts/imviz_composite_subset_api.ipynb create mode 100644 notebooks/concepts/specviz_composite_subset_api.ipynb diff --git a/.gitignore b/.gitignore index e804983ce8..96227b3b31 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ htmlcov MANIFEST .ipynb_checkpoints notebooks/*.fits +notebooks/*.ecsv # Sphinx docs/api diff --git a/notebooks/concepts/imviz_composite_subset_api.ipynb b/notebooks/concepts/imviz_composite_subset_api.ipynb new file mode 100644 index 0000000000..780a4e2079 --- /dev/null +++ b/notebooks/concepts/imviz_composite_subset_api.ipynb @@ -0,0 +1,214 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "98cfb0f9-9995-4530-b6d0-3cbe41055d9c", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from regions import CirclePixelRegion, PixCoord\n", + "\n", + "from jdaviz import Imviz" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "473cf04d-0550-4c16-afce-f26d87d91002", + "metadata": {}, + "outputs": [], + "source": [ + "a = np.random.random((100, 100))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8096c7b-d3e1-472b-935d-770af8c0fd07", + "metadata": {}, + "outputs": [], + "source": [ + "imviz = Imviz()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2b7b2b4-215e-4f58-b01c-c65a7eabc1fe", + "metadata": {}, + "outputs": [], + "source": [ + "imviz.load_data(a, data_label=\"a\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc3dd6fb-6d1f-4792-9199-3742e60849f8", + "metadata": {}, + "outputs": [], + "source": [ + "imviz.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4dfbfc9a-20bc-43fb-95b5-75a008785708", + "metadata": {}, + "outputs": [], + "source": [ + "imviz.default_viewer.reset_limits()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00e0b637-983e-4eb9-9941-c88115a56baf", + "metadata": {}, + "outputs": [], + "source": [ + "aper_1 = CirclePixelRegion(center=PixCoord(x=42, y=43), radius=4.2)\n", + "aper_2 = CirclePixelRegion(center=PixCoord(x=10, y=20), radius=3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b075437-bb24-449c-b87f-fec42d5ff2a8", + "metadata": {}, + "outputs": [], + "source": [ + "plg_subset = imviz.plugins[\"Subset Tools\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42d35bcb-1855-4b1b-8843-d6324e3836af", + "metadata": {}, + "outputs": [], + "source": [ + "plg_subset.import_region([aper_1, aper_2], combination_mode=\"or\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6806b7b0-4252-4c75-a306-09b6cc756e37", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export = imviz.plugins[\"Export\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8fe64fa-02d5-4b96-a6fc-41a1a4828bb2", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export.subset = \"Subset 1\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef01d881-712b-4f19-ad81-ac6c627df64c", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export.subset_format = \"reg\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d408fe0f-12e4-4f89-8aff-1e1abc61862d", + "metadata": {}, + "outputs": [], + "source": [ + "# NotImplementedError: Subset can not be exported - Export for composite subsets not yet supported.\n", + "plg_export.export()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aac2f285-a7ab-4177-8d92-bea6cb7e1306", + "metadata": {}, + "outputs": [], + "source": [ + "# {'Subset 1': [\n", + "# ,\n", + "# ]}\n", + "imviz.app.get_subsets(object_only=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80e4b0be-21ff-474b-b3e0-3251a8bd1e0e", + "metadata": {}, + "outputs": [], + "source": [ + "# {'Subset 1': [{'name': 'CircularROI',\n", + "# 'glue_state': 'RoiSubsetState',\n", + "# 'region': ,\n", + "# 'sky_region': None,\n", + "# 'subset_state': },\n", + "# {'name': 'CircularROI',\n", + "# 'glue_state': 'OrState',\n", + "# 'region': ,\n", + "# 'sky_region': None,\n", + "# 'subset_state': }]}\n", + "imviz.app.get_subsets()" + ] + }, + { + "cell_type": "markdown", + "id": "3bc553f9-c0fe-4989-82fe-7688d3c0ceed", + "metadata": {}, + "source": [ + "### Additional work\n", + "\n", + "How to store `glue_state` into a file? How will this conflict with existing `regions` I/O support? We cannot ask `regions` to support `glue_state` natively because it is a property very specific to `glue` only.\n", + "\n", + "However, we could utilize [compound region](https://astropy-regions.readthedocs.io/en/latest/compound.html).\n", + "\n", + "* Pros: Out of the box support from `regions` for AND, OR (a.k.a. ADD), XOR.\n", + "* Cons: No concept of REPLACE and REMOVE. `photutils` does not understand compound apertures, so even if `regions` can load them, they are useless for photometry (not a new problem).\n", + "\n", + "### Recommendations\n", + "\n", + "1. Acknowledge that this feature will never be used for aperture photometry.\n", + "2. Create follow-up ticket to add support for [compound region](https://astropy-regions.readthedocs.io/en/latest/compound.html).\n", + "3. Create follow-up ticket to then investigate REPLACE and REMOVE, after (1) is implemented." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/concepts/specviz_composite_subset_api.ipynb b/notebooks/concepts/specviz_composite_subset_api.ipynb new file mode 100644 index 0000000000..d8a85ff042 --- /dev/null +++ b/notebooks/concepts/specviz_composite_subset_api.ipynb @@ -0,0 +1,258 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "4f2841a4-9ba6-4238-9243-8f2a8873ceec", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from astropy import units as u\n", + "from specutils import Spectrum1D, SpectralRegion\n", + "\n", + "from jdaviz import Specviz" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99f14f77-a6db-457d-8c27-e74d3a845059", + "metadata": {}, + "outputs": [], + "source": [ + "wave = np.linspace(1, 5, 100) * u.um\n", + "flux = np.random.random(wave.shape) * u.nJy\n", + "sp = Spectrum1D(flux=flux, spectral_axis=wave)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13f27c01-ab03-44ae-b386-f754d44a8148", + "metadata": {}, + "outputs": [], + "source": [ + "specviz = Specviz()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b548a4e-5c9e-4764-8426-e9e2bd6d40bd", + "metadata": {}, + "outputs": [], + "source": [ + "specviz.load_data(sp, data_label=\"Spectrum\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb8cee0d-6bd3-4b62-bf87-13a8727f21d3", + "metadata": {}, + "outputs": [], + "source": [ + "specviz.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "282eafc9-a148-44f4-832b-e0dab78df76b", + "metadata": {}, + "outputs": [], + "source": [ + "plg_subset = specviz.plugins[\"Subset Tools\"]" + ] + }, + { + "cell_type": "raw", + "id": "4a26ecfb-ad98-4add-8cb2-98e9ff33763a", + "metadata": {}, + "source": [ + "# This works\n", + "spreg_1 = SpectralRegion(1.5 * u.um, 2 * u.um)\n", + "spreg_2 = SpectralRegion(3 * u.um, 3.5 * u.um)\n", + "plg_subset.import_region([spreg_1, spreg_2], combination_mode=\"or\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b2092ae-4638-4bec-9e56-4c8109055fcc", + "metadata": {}, + "outputs": [], + "source": [ + "# This kinda works because Export simplifies it to\n", + "# lower_bound upper_bound\n", + "# 1.5 3.0\n", + "# 3.5 4.5\n", + "#\n", + "# Same output but original values lost.\n", + "spreg_1 = SpectralRegion(1.5 * u.um, 4.5 * u.um)\n", + "spreg_2 = SpectralRegion(3 * u.um, 3.5 * u.um)\n", + "plg_subset.import_region([spreg_1, spreg_2], combination_mode=\"xor\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e41755b2-fe38-4342-a279-eb728867928f", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export = specviz.plugins[\"Export\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67b29aec-78b1-450f-9847-4303874a1cfa", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export.subset = \"Subset 1\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d4dd45a-c6a0-4e19-bf62-44fe671e6302", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export.subset_format = \"ecsv\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "482515ec-c87e-4460-a46e-205703809a2a", + "metadata": {}, + "outputs": [], + "source": [ + "plg_export.export(overwrite=True) # Subset_1.ecsv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "534d36ed-3a45-49fb-b005-4185d4e2d9e4", + "metadata": {}, + "outputs": [], + "source": [ + "!cat Subset_1.ecsv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07db40cd-ba74-4bf4-b343-48275be38f60", + "metadata": {}, + "outputs": [], + "source": [ + "specviz_2 = Specviz()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "014a7856-20f9-4670-a424-38c0c27b46fa", + "metadata": {}, + "outputs": [], + "source": [ + "specviz_2.load_data(sp, data_label=\"Spectrum\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a6e3c1e-8499-44ec-b5f2-01cd0d822571", + "metadata": {}, + "outputs": [], + "source": [ + "specviz_2.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e5c556c-675c-49f7-a4d0-8e0903d3384c", + "metadata": {}, + "outputs": [], + "source": [ + "plg_subset_2 = specviz_2.plugins[\"Subset Tools\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83f54337-e98c-4e81-ae34-396448240d7e", + "metadata": {}, + "outputs": [], + "source": [ + "# Because ECSV output was simplified, the combination_mode can break\n", + "# things if you are not careful!\n", + "#plg_subset_2.import_region(\"Subset_1.ecsv\") # BAD -- first subregion missing\n", + "plg_subset_2.import_region(\"Subset_1.ecsv\", combination_mode=\"or\") # OK\n", + "#plg_subset_2.import_region(\"Subset_1.ecsv\", combination_mode=\"and\") # BAD -- null subset\n", + "#plg_subset_2.import_region(\"Subset_1.ecsv\", combination_mode=\"new\") # BAD -- two subsets" + ] + }, + { + "cell_type": "markdown", + "id": "190c99e2-ee01-4f11-8d74-03d2c43c56c5", + "metadata": {}, + "source": [ + "### Additional work\n", + "\n", + "If we want perfect roundtrip, to do it properly, we need an equivalent of\n", + "[compound region](https://astropy-regions.readthedocs.io/en/latest/compound.html) for\n", + "[SpectralRegion](https://specutils.readthedocs.io/en/stable/spectral_regions.html) upstream.\n", + "This could take significant effort, so we might have to just acknowledge the limitations\n", + "and move on. Is keeping original inputs that important if simplified version still give\n", + "you the same thing?\n", + "\n", + "If you want support for arbitrary operators from the same ECSV file, this is not possible\n", + "without native operator support in `specutils` (see above).\n", + "\n", + "The `import_region` API can have \"wild\" behaviors depending on `combination_mode`\n", + "when a filename is given. This needs to be reined in.\n", + "\n", + "### Recommendations\n", + "\n", + "1. Acknowledge that original inputs might be lost upon saving it back out\n", + " because we simplify the composite spectral regions.\n", + "2. Acknowledge that one file can only store one subset.\n", + " (Needs extra pondering if you also want to support `\"new\"` to create one subset\n", + " per file entry.)\n", + "4. Only allow `combination_mode=\"or\"` when a filename is given.\n", + " Since default is actually `None` (which `glue` translated to `\"replace\"`,\n", + " maybe have to recode `None` to mean `\"or\"` when input is filename.\n", + " Raise exception for any other value." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}