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

Add documentation section and example for using as_mpl_selector with callback function #419

Merged
merged 1 commit into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions docs/masks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,54 @@ statistics::
>>> import numpy as np
>>> np.average(data, weights=mask) # doctest: +FLOAT_CMP
9364.012674888021


.. _interactive-masks:

Interactive Mask Control
------------------------

In the last example we will show how to use a
:ref:`Matplotlib selector<regions-as_mpl_selector>` widget with a custom
``callback`` function for creating a mask and updating it interactively through
the selector.
We first create an :class:`~regions.EllipsePixelRegion` and add an ``as_mpl_selector``
property linked to the Matplotlib axes. This can be moved around to
position it on different sources, and resized just like its Rectangle
counterpart, using the handles of the bounding box.

The user-defined callback function here generates a mask from this region and overlays
it on the image as an alpha filter (keeping the areas outside shaded).
We will use this mask as an aperture as well to calculate integrated
and averaged flux, which is updated live in the text field of the plot as well.

.. plot::
:context:
:include-source:
:align: center

from astropy import units as u
from regions import PixCoord, EllipsePixelRegion

hdulist = fits.open(filename)
hdu = hdulist[0]

plt.clf()
ax = plt.subplot(1, 1, 1)
im = ax.imshow(hdu.data, cmap=plt.cm.viridis, interpolation='nearest', origin='lower')
text = ax.text(122, 1002, '', size='small', color='yellow')
ax.set_xlim(120, 180)
ax.set_ylim(1000, 1059)

def update_sel(region):
mask = region.to_mask(mode='subpixels', subpixels=10)
im.set_alpha((mask.to_image(hdu.data.shape) + 1) / 2)
total = mask.multiply(hdu.data).sum()
mean = np.average(hdu.data, weights=mask.to_image(hdu.data.shape))
text.set_text(f'Total: {total:g}\nMean: {mean:g}')

ellipse = EllipsePixelRegion(center=PixCoord(x=126, y=1031), width=8, height=4,
angle=-0*u.deg, visual={'color': 'yellow'})
selector = ellipse.as_mpl_selector(ax, callback=update_sel, use_data_coordinates=True)

hdulist.close()
46 changes: 46 additions & 0 deletions docs/shapes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,52 @@ to a :class:`~regions.SkyRegion`, call the
radius: 18.55481729935556 arcsec


.. _regions-as_mpl_selector:

Selectors for Regions
---------------------

Several geometric regions (at this time, :class:`~regions.RectanglePixelRegion`,
:class:`~regions.EllipsePixelRegion` and :class:`~regions.PolygonPixelRegion`)
provide a method :meth:`~regions.RectanglePixelRegion.as_mpl_selector` to
create an interactive editable matplotlib widget that will be
connected to its parent region.

.. plot::
:context:
:include-source:

import matplotlib.pyplot as plt
import numpy as np
from regions import PixCoord, RectanglePixelRegion

x, y = np.mgrid[-15:16, -10:21]
z = np.exp(-(x / 4)**2 - (y / 6)**2)
ax = plt.subplot()
img = ax.imshow(z)

rectangle = RectanglePixelRegion(center=PixCoord(x=12, y=15), width=14, height=10)
selector = rectangle.as_mpl_selector(ax)

The ``selector`` creates and establishes a link to a matplotlib ``Selector``
widget that allows manually positioning the ``rectangle`` region at the
central point, and scaling it by dragging its corner points.
Several modifier keys as specified by the ``state_modifier_keys`` parameter to
:class:`matplotlib.widgets.RectangleSelector` provide further control of the
manipulation of this widget, with the following operations available:

- "move": Move the existing shape from anywhere, default: "space".
- "clear": Clear the current shape, default: "escape".
- "square": Make the shape square, default: "shift".
- "center": Change the shape around its center, default: "ctrl".
- "rotate": Rotate the shape around its center, default: "r" (toggles, requires Matplotlib 3.6+).

Via the optional ``callback`` parameter this method can be passed a
custom function that will be called on every update of the region,
i.e. after every move or resize of the selector.
For an example of its usage see :ref:`Interactive Mask Control<interactive-masks>`.


Multiple Regions
----------------

Expand Down