Source code for jdaviz.core.region_translators

"""The ``region_translators`` module houses translations of
:ref:`regions:shapes` to :ref:`photutils:photutils-aperture` apertures.
"""
from astropy import units as u
from astropy.coordinates import Angle
from glue.core.roi import CircularROI, EllipticalROI, RectangularROI, CircularAnnulusROI
from photutils.aperture import (CircularAperture, SkyCircularAperture,
                                EllipticalAperture, SkyEllipticalAperture,
                                RectangularAperture, SkyRectangularAperture,
                                CircularAnnulus, SkyCircularAnnulus,
                                EllipticalAnnulus, SkyEllipticalAnnulus,
                                RectangularAnnulus, SkyRectangularAnnulus)
from regions import (CirclePixelRegion, CircleSkyRegion,
                     EllipsePixelRegion, EllipseSkyRegion,
                     RectanglePixelRegion, RectangleSkyRegion,
                     CircleAnnulusPixelRegion, CircleAnnulusSkyRegion,
                     EllipseAnnulusPixelRegion, EllipseAnnulusSkyRegion,
                     RectangleAnnulusPixelRegion, RectangleAnnulusSkyRegion, PixCoord)

__all__ = ['regions2roi', 'regions2aperture', 'aperture2regions']


def _get_region_from_spatial_subset(plugin_obj, subset_label):
    """Convert a ``glue`` ROI in the given subset to ``regions`` shape.

    .. note:: This is for internal use only in Imviz plugins.

    Parameters
    ----------
    plugin_obj : obj
        Plugin instance that needs this translation.
        The plugin is assumed to have a special setup that gives
        it access to these attributes: ``app`` and ``dataset_selected``.
        The ``app._jdaviz_helper.default_viewer`` attribute must also
        exist and point to an image viewer that has a ``_get_real_xy``
        method.

    subset_label : str
        Label of selected subset in the plugin.

    Returns
    -------
    reg : `regions.Region`
        An equivalent ``regions`` shape.

    Raises
    ------
    ValueError
        Subset is not found.

    See Also
    --------
    regions2roi

    """
    for subset_grp in plugin_obj.app.data_collection.subset_groups:
        if subset_grp.label == subset_label:
            for sbst in subset_grp.subsets:
                if sbst.data.label == plugin_obj.dataset_selected:
                    reg = sbst.data.get_selection_definition(
                        subset_id=subset_label, format='astropy-regions')
                    # Works around https://github.com/glue-viz/glue-astronomy/issues/52
                    # Assume it is always pixel region, not sky region. Even with multiple
                    # viewers, they all seem to share the same reference image even when it is
                    # not loaded in all the viewers, so use default viewer.
                    viewer = plugin_obj.app._jdaviz_helper.default_viewer

                    x, y, _, _ = viewer._get_real_xy(
                        plugin_obj.app.data_collection[plugin_obj.dataset_selected],
                        reg.center.x, reg.center.y)
                    reg.center.x = x
                    reg.center.y = y
                    return reg
    else:
        raise ValueError(f'Subset "{subset_label}" not found')


[docs]def regions2roi(region_shape, wcs=None): """Convert a given ``regions`` shape to ``glue`` ROI. This is the opposite of what is offered by ``glue_astronomy.translators.regions.AstropyRegionsHandler.to_object`` but does not cover all the same shapes exactly. Parameters ---------- region_shape : `regions.Region` A supported ``regions`` shape. wcs : `~astropy.wcs.WCS` or `None` A compatible WCS object, if required. **This is only used for sky aperture.** Returns ------- roi : `glue.core.roi.Roi` An equivalent ``glue`` ROI. Raises ------ ValueError WCS is required but not provided. NotImplementedError The given ``regions`` shape is not supported. Examples -------- Translate a `regions.CirclePixelRegion` to `glue.core.roi.CircularROI`: >>> from regions import CirclePixelRegion, PixCoord >>> from jdaviz.core.region_translators import regions2roi >>> region_shape = CirclePixelRegion(center=PixCoord(x=42, y=43), radius=4.2) >>> regions2roi(region_shape) # doctest: +ELLIPSIS <glue.core.roi.CircularROI object at ...> """ if isinstance(region_shape, (CircleSkyRegion, EllipseSkyRegion, RectangleSkyRegion, CircleAnnulusSkyRegion)): if wcs is None: raise ValueError(f'WCS must be provided for {region_shape}') # Convert sky to pixel region first, if necessary. region_shape = region_shape.to_pixel(wcs) if isinstance(region_shape, CirclePixelRegion): roi = CircularROI( xc=region_shape.center.x, yc=region_shape.center.y, radius=region_shape.radius) elif isinstance(region_shape, EllipsePixelRegion): roi = EllipticalROI( xc=region_shape.center.x, yc=region_shape.center.y, radius_x=region_shape.width * 0.5, radius_y=region_shape.height * 0.5, theta=region_shape.angle.to_value(u.radian)) elif isinstance(region_shape, RectanglePixelRegion): half_w = region_shape.width * 0.5 half_h = region_shape.height * 0.5 xmin = region_shape.center.x - half_w xmax = region_shape.center.x + half_w ymin = region_shape.center.y - half_h ymax = region_shape.center.y + half_h roi = RectangularROI( xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, theta=region_shape.angle.to_value(u.radian)) elif isinstance(region_shape, CircleAnnulusPixelRegion): roi = CircularAnnulusROI( xc=region_shape.center.x, yc=region_shape.center.y, inner_radius=region_shape.inner_radius, outer_radius=region_shape.outer_radius) else: raise NotImplementedError(f'{region_shape.__class__.__name__} is not supported') return roi
[docs]def regions2aperture(region_shape): """Convert a given ``regions`` shape to ``photutils`` aperture. Parameters ---------- region_shape : `regions.Region` A supported ``regions`` shape. Returns ------- aperture : `photutils.aperture.Aperture` An equivalent ``photutils`` aperture. Raises ------ NotImplementedError The given ``regions`` shape is not supported. Examples -------- Translate a `regions.CirclePixelRegion` to `photutils.aperture.CircularAperture`: >>> from regions import CirclePixelRegion, PixCoord >>> from jdaviz.core.region_translators import regions2aperture >>> region_shape = CirclePixelRegion(center=PixCoord(x=42, y=43), radius=4.2) >>> regions2aperture(region_shape) <CircularAperture([42., 43.], r=4.2)> See Also -------- aperture2regions """ if isinstance(region_shape, CirclePixelRegion): aperture = CircularAperture(region_shape.center.xy, region_shape.radius) elif isinstance(region_shape, CircleSkyRegion): aperture = SkyCircularAperture(region_shape.center, region_shape.radius) elif isinstance(region_shape, EllipsePixelRegion): aperture = EllipticalAperture( region_shape.center.xy, region_shape.width * 0.5, region_shape.height * 0.5, theta=region_shape.angle.to_value(u.radian)) elif isinstance(region_shape, EllipseSkyRegion): aperture = SkyEllipticalAperture( region_shape.center, region_shape.width * 0.5, region_shape.height * 0.5, theta=region_shape.angle) elif isinstance(region_shape, RectanglePixelRegion): aperture = RectangularAperture( region_shape.center.xy, region_shape.width, region_shape.height, theta=region_shape.angle.to_value(u.radian)) elif isinstance(region_shape, RectangleSkyRegion): aperture = SkyRectangularAperture( region_shape.center, region_shape.width, region_shape.height, theta=region_shape.angle) elif isinstance(region_shape, CircleAnnulusPixelRegion): aperture = CircularAnnulus( region_shape.center.xy, region_shape.inner_radius, region_shape.outer_radius) elif isinstance(region_shape, CircleAnnulusSkyRegion): aperture = SkyCircularAnnulus( region_shape.center, region_shape.inner_radius, region_shape.outer_radius) elif isinstance(region_shape, EllipseAnnulusPixelRegion): aperture = EllipticalAnnulus( region_shape.center.xy, region_shape.inner_width * 0.5, region_shape.outer_width * 0.5, region_shape.outer_height * 0.5, b_in=region_shape.inner_height * 0.5, theta=region_shape.angle.to_value(u.radian)) elif isinstance(region_shape, EllipseAnnulusSkyRegion): aperture = SkyEllipticalAnnulus( region_shape.center, region_shape.inner_width * 0.5, region_shape.outer_width * 0.5, region_shape.outer_height * 0.5, b_in=region_shape.inner_height * 0.5, theta=region_shape.angle) elif isinstance(region_shape, RectangleAnnulusPixelRegion): aperture = RectangularAnnulus( region_shape.center.xy, region_shape.inner_width, region_shape.outer_width, region_shape.outer_height, h_in=region_shape.inner_height, theta=region_shape.angle.to_value(u.radian)) elif isinstance(region_shape, RectangleAnnulusSkyRegion): aperture = SkyRectangularAnnulus( region_shape.center, region_shape.inner_width, region_shape.outer_width, region_shape.outer_height, h_in=region_shape.inner_height, theta=region_shape.angle) else: raise NotImplementedError(f'{region_shape.__class__.__name__} is not supported') return aperture
[docs]def aperture2regions(aperture): """Convert a given ``photutils`` aperture to ``regions`` shape. Parameters ---------- aperture : `photutils.aperture.Aperture` An equivalent ``photutils`` aperture. Returns ------- region_shape : `regions.Region` A supported ``regions`` shape. Raises ------ NotImplementedError The given ``photutils`` aperture is not supported. ValueError Invalid inputs. Examples -------- Translate a `photutils.aperture.CircularAperture` to `regions.CirclePixelRegion`: >>> from photutils.aperture import CircularAperture >>> from jdaviz.core.region_translators import aperture2regions >>> aperture = CircularAperture((42, 43), 4.2) >>> aperture2regions(aperture) <CirclePixelRegion(center=PixCoord(x=42.0, y=43.0), radius=4.2)> See Also -------- regions2aperture """ if isinstance(aperture, CircularAperture): region_shape = CirclePixelRegion( center=positions2pixcoord(aperture.positions), radius=aperture.r) elif isinstance(aperture, SkyCircularAperture): region_shape = CircleSkyRegion(center=aperture.positions, radius=aperture.r) elif isinstance(aperture, EllipticalAperture): region_shape = EllipsePixelRegion( center=positions2pixcoord(aperture.positions), width=aperture.a * 2, height=aperture.b * 2, angle=theta2angle(aperture.theta)) elif isinstance(aperture, SkyEllipticalAperture): region_shape = EllipseSkyRegion( center=aperture.positions, width=aperture.a * 2, height=aperture.b * 2, angle=aperture.theta) elif isinstance(aperture, RectangularAperture): region_shape = RectanglePixelRegion( center=positions2pixcoord(aperture.positions), width=aperture.w, height=aperture.h, angle=theta2angle(aperture.theta)) elif isinstance(aperture, SkyRectangularAperture): region_shape = RectangleSkyRegion( center=aperture.positions, width=aperture.w, height=aperture.h, angle=aperture.theta) elif isinstance(aperture, CircularAnnulus): region_shape = CircleAnnulusPixelRegion( center=positions2pixcoord(aperture.positions), inner_radius=aperture.r_in, outer_radius=aperture.r_out) elif isinstance(aperture, SkyCircularAnnulus): region_shape = CircleAnnulusSkyRegion( center=aperture.positions, inner_radius=aperture.r_in, outer_radius=aperture.r_out) elif isinstance(aperture, EllipticalAnnulus): region_shape = EllipseAnnulusPixelRegion( center=positions2pixcoord(aperture.positions), inner_width=aperture.a_in * 2, inner_height=aperture.b_in * 2, outer_width=aperture.a_out * 2, outer_height=aperture.b_out * 2, angle=theta2angle(aperture.theta)) elif isinstance(aperture, SkyEllipticalAnnulus): region_shape = EllipseAnnulusSkyRegion( center=aperture.positions, inner_width=aperture.a_in * 2, inner_height=aperture.b_in * 2, outer_width=aperture.a_out * 2, outer_height=aperture.b_out * 2, angle=aperture.theta) elif isinstance(aperture, RectangularAnnulus): region_shape = RectangleAnnulusPixelRegion( center=positions2pixcoord(aperture.positions), inner_width=aperture.w_in, inner_height=aperture.h_in, outer_width=aperture.w_out, outer_height=aperture.h_out, angle=theta2angle(aperture.theta)) elif isinstance(aperture, SkyRectangularAnnulus): region_shape = RectangleAnnulusSkyRegion( center=aperture.positions, inner_width=aperture.w_in, inner_height=aperture.h_in, outer_width=aperture.w_out, outer_height=aperture.h_out, angle=aperture.theta) else: # pragma: no cover raise NotImplementedError(f'{aperture.__class__.__name__} is not supported') return region_shape
def positions2pixcoord(positions): """Convert ``photutils`` aperture positions to `~regions.PixCoord` that is acceptable by ``regions`` shape. """ if positions.shape != (2, ): raise ValueError('regions shape only accepts scalar x and y positions') if isinstance(positions, u.Quantity): pixcoord = PixCoord(x=positions[0].value, y=positions[1].value) else: pixcoord = PixCoord(x=positions[0], y=positions[1]) return pixcoord def theta2angle(theta): """Convert ``photutils`` theta to ``regions`` angle.""" return Angle(theta, u.radian)