Skip to content

Commit 5a2431b

Browse files
committed
DOC: add section for as_mpl_selector and callback example to "masks"
1 parent 521a460 commit 5a2431b

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

docs/masks.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,54 @@ statistics::
273273
>>> import numpy as np
274274
>>> np.average(data, weights=mask) # doctest: +FLOAT_CMP
275275
9364.012674888021
276+
277+
278+
.. _interactive-masks:
279+
280+
Interactive Mask Control
281+
------------------------
282+
283+
In the last example we will show how to use a
284+
:ref:`Matplotlib selector<regions-as_mpl_selector>` widget with a custom
285+
``callback`` function for creating a mask and updating it interactively through
286+
the selector.
287+
We first create an :class:`~regions.EllipsePixelRegion` and add an ``as_mpl_selector``
288+
property linked to the Matplotlib axes. This can be moved around to
289+
position it on different sources, and resized just like its Rectangle
290+
counterpart, using the handles of the bounding box.
291+
292+
The user-defined callback function here generates a mask from this region and overlays
293+
it on the image as an alpha filter (keeping the areas outside shaded).
294+
We will use this mask as an aperture as well to calculate integrated
295+
and averaged flux, which is updated live in the text field of the plot as well.
296+
297+
.. plot::
298+
:context:
299+
:include-source:
300+
:align: center
301+
302+
from astropy import units as u
303+
from regions import PixCoord, EllipsePixelRegion
304+
305+
hdulist = fits.open(filename)
306+
hdu = hdulist[0]
307+
308+
plt.clf()
309+
ax = plt.subplot(1, 1, 1)
310+
im = ax.imshow(hdu.data, cmap=plt.cm.viridis, interpolation='nearest', origin='lower')
311+
text = ax.text(122, 1002, '', size='small', color='yellow')
312+
ax.set_xlim(120, 180)
313+
ax.set_ylim(1000, 1059)
314+
315+
def update_sel(region):
316+
mask = region.to_mask(mode='subpixels', subpixels=10)
317+
im.set_alpha((mask.to_image(hdu.data.shape) + 1) / 2)
318+
total = mask.multiply(hdu.data).sum()
319+
mean = np.average(hdu.data, weights=mask.to_image(hdu.data.shape))
320+
text.set_text(f'Total: {total:g}\nMean: {mean:g}')
321+
322+
ellipse = EllipsePixelRegion(center=PixCoord(x=126, y=1031), width=8, height=4,
323+
angle=-0*u.deg, visual={'color': 'yellow'})
324+
selector = ellipse.as_mpl_selector(ax, callback=update_sel, use_data_coordinates=True)
325+
326+
hdulist.close()

docs/shapes.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,48 @@ to a :class:`~regions.SkyRegion`, call the
272272
radius: 18.55481729935556 arcsec
273273
274274
275+
.. _regions-as_mpl_selector:
276+
277+
Selectors for Regions
278+
---------------------
279+
280+
Several geometric regions (at this time, :class:`~regions.RectanglePixelRegion`,
281+
:class:`~regions.EllipsePixelRegion` and :class:`~regions.PolygonPixelRegion`)
282+
provide a method :meth:`~regions.RectanglePixelRegion.as_mpl_selector` to
283+
create an interactive editable matplotlib widget that will be
284+
connected to its parent region.
285+
286+
.. doctest-skip::
287+
288+
>>> import matplotlib.pyplot as plt
289+
>>> import numpy as np
290+
>>> from regions import PixCoord, RectanglePixelRegion
291+
>>> x, y = np.mgrid[-15:16, -10:21]
292+
>>> z = np.exp(-(x / 4)**2 - (y / 6)**2)
293+
>>> ax = plt.subplot()
294+
>>> img = ax.imshow(z)
295+
>>> rectangle = RectanglePixelRegion(center=PixCoord(x=12, y=15), width=12, height=10)
296+
>>> selector = rectangle.as_mpl_selector(ax)
297+
298+
The ``selector`` creates and establcihes a link to a ``matplotlib`` ``Selector``
299+
widget that allows manually positioning the ``rectangle`` region at the
300+
central point, and scaling it by dragging its corner points.
301+
Several modifier keys as specified by the ``state_modifier_keys`` parameter to
302+
:class:`matplotlib.widgets.RectangleSelector` provide further control of the
303+
manipulation of this widget, with the following operations available:
304+
305+
- "move": Move the existing shape from anywhere, default: "space".
306+
- "clear": Clear the current shape, default: "escape".
307+
- "square": Make the shape square, default: "shift".
308+
- "center": Change the shape around its center, default: "ctrl".
309+
- "rotate": Rotate the shape around its center, default: "r" (toggles, requires Matplotlib 3.6+).
310+
311+
Via the optional ``callback`` parameter this method can be passed a
312+
custom function that will be called on every update of the region,
313+
i.e. after every move or resize of the selector.
314+
For an example of its usage see :ref:`Interactive Mask Control<interactive-masks>`.
315+
316+
275317
Multiple Regions
276318
----------------
277319

0 commit comments

Comments
 (0)