diff --git a/docs/masks.rst b/docs/masks.rst index e74dca23..01cf4f99 100644 --- a/docs/masks.rst +++ b/docs/masks.rst @@ -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` 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() diff --git a/docs/shapes.rst b/docs/shapes.rst index 492c6da8..6971e9e1 100644 --- a/docs/shapes.rst +++ b/docs/shapes.rst @@ -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`. + + Multiple Regions ----------------