Source code for ginga.util.ap_region

#
# ap_region.py -- AstroPy regions support
#
# This is open-source software licensed under a BSD license.
# Please see the file LICENSE.txt for details.
#
"""
This module provides Ginga support for DS9 type region files and objects via
the Astropy ``regions`` module.
"""
import numpy as np

from ginga.canvas.CanvasObject import get_canvas_types

from astropy import units as u

HAVE_REGIONS = False
try:
    import regions
    HAVE_REGIONS = True
except ImportError:
    pass


__all__ = ['astropy_region_to_ginga_canvas_object', 'add_region',
           'ginga_canvas_object_to_astropy_region']


# mappings of point styles
pt_ginga = dict(square='*', cross='x', plus='+', diamond='D')
pt_regions = {v: k for k, v in pt_ginga.items()}


[docs]def astropy_region_to_ginga_canvas_object(r): """ Convert an Astropy region object to a Ginga canvas object. Parameters ---------- r : subclass of `~regions.PixelRegion` The region object to be converted Returns ------- obj : subclass of `~ginga.canvas.CanvasObject` The corresponding Ginga canvas object """ if not HAVE_REGIONS: raise ValueError("Please install the Astropy 'regions' package to use this function") dc = get_canvas_types() obj = None if isinstance(r, (regions.CirclePixelRegion,)): obj = dc.Circle(r.center.x, r.center.y, r.radius) elif isinstance(r, (regions.EllipsePixelRegion,)): obj = dc.Ellipse(r.center.x, r.center.y, r.width / 2, r.height / 2., rot_deg=r.angle.to(u.deg).value) # NOTE: need to check for Text before Point, because Text seems to be # a subclass of Point in regions elif isinstance(r, (regions.TextPixelRegion,)): # NOTE: font needed here, but will be overridden later if specified # in the region's visuals obj = dc.Text(r.center.x, r.center.y, text=r.text, font='sans', rot_deg=float(r.visual.get('textangle', 0.0))) elif isinstance(r, (regions.PointPixelRegion,)): # what is a reasonable default radius? radius = 15 style = r.visual.get('symbol', '*') style = pt_regions.get(style, 'square') obj = dc.Point(r.center.x, r.center.y, radius, style=style) elif isinstance(r, (regions.LinePixelRegion,)): obj = dc.Line(r.start.x, r.start.y, r.end.x, r.end.y) if r.meta.get('line', '0') == '1': obj.arrow = 'both' elif isinstance(r, (regions.RectanglePixelRegion,)): obj = dc.Box(r.center.x, r.center.y, r.width / 2, r.height / 2., rot_deg=r.angle.to(u.deg).value) elif isinstance(r, (regions.PolygonPixelRegion,)): points = np.array(r.vertices.xy).T obj = dc.Polygon(points) elif isinstance(r, (regions.CircleAnnulusPixelRegion,)): rin = r.inner_radius rout = r.outer_radius wd = rout - rin obj = dc.Annulus(r.center.x, r.center.y, rin, width=wd, atype='circle') elif isinstance(r, (regions.EllipseAnnulusPixelRegion,)): xwd = (r.outer_width - r.inner_width) / 2.0 ywd = (r.outer_height - r.inner_height) / 2.0 obj = dc.Annulus2R(r.center.x, r.center.y, r.inner_width / 2.0, r.inner_height / 2.0, xwidth=xwd, ywidth=ywd, atype='ellipse', rot_deg=r.angle.to(u.deg).value) elif isinstance(r, (regions.RectangleAnnulusPixelRegion,)): xwd = (r.outer_width - r.inner_width) / 2.0 ywd = (r.outer_height - r.inner_height) / 2.0 obj = dc.Annulus2R(r.center.x, r.center.y, r.inner_width / 2.0, r.inner_height / 2.0, xwidth=xwd, ywidth=ywd, atype='box', rot_deg=r.angle.to(u.deg).value) else: raise ValueError("Don't know how to convert this object") # Set visual styling attributes obj.color = r.visual.get('color', 'green') if hasattr(obj, 'font'): obj.font = r.visual.get('font', 'Sans') if 'fontsize' in r.visual: obj.fontsize = int(r.visual['fontsize']) if hasattr(obj, 'linewidth'): obj.linewidth = r.visual.get('linewidth', 1) if hasattr(obj, 'fill'): obj.fill = r.visual.get('fill', False) # Limited support for other metadata obj.editable = r.meta.get('edit', True) obj.set_data(name=r.meta.get('name', None)) # needed for compound objects like annulus obj.sync_state() return obj
[docs]def add_region(canvas, r, tag=None, redraw=True): """ Convenience function to plot an Astropy regions object on a Ginga canvas. Parameters ---------- canvas : `~ginga.canvas.types.layer.DrawingCanvas` The Ginga canvas on which the region should be plotted. r : subclass of `~regions.PixelRegion` The region object to be plotted tag : str or None (optional, default: None) Caller can optionally pass a specific tag for the canvas object redraw : bool (optional, default: True) True if the viewers of the canvas should be updated immediately """ obj = astropy_region_to_ginga_canvas_object(r) if tag is None: tag = obj.get_data('name') if obj is not None: canvas.add(obj, tag=tag, redraw=redraw) return obj
[docs]def ginga_canvas_object_to_astropy_region(obj): """ Convert a Ginga canvas object to an AstroPy region object. Parameters ---------- obj : subclass of `~ginga.canvas.CanvasObject` The Ginga canvas object to be converted Returns ------- r : subclass of `~regions.PixelRegion` The corresponding AstroPy region object """ if not HAVE_REGIONS: raise ValueError("Please install the Astropy 'regions' package to use this function") dc = get_canvas_types() r = None if isinstance(obj, (dc.Circle,)): r = regions.CirclePixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), radius=obj.radius) elif isinstance(obj, (dc.Ellipse,)): r = regions.EllipsePixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), width=obj.xradius * 2, height=obj.yradius * 2, angle=obj.rot_deg * u.deg) elif isinstance(obj, (dc.Text,)): r = regions.TextPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), text=obj.text) r.visual['textangle'] = str(obj.rot_deg) elif isinstance(obj, (dc.Point,)): r = regions.PointPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y)) style = pt_ginga.get(obj.style, '*') r.visual['symbol'] = style elif isinstance(obj, (dc.Line,)): r = regions.LinePixelRegion(start=regions.PixCoord(x=obj.x1, y=obj.y1), end=regions.PixCoord(x=obj.x2, y=obj.y2)) elif isinstance(obj, (dc.Box,)): r = regions.RectanglePixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), width=obj.xradius * 2, height=obj.yradius * 2, angle=obj.rot_deg * u.deg) elif isinstance(obj, (dc.Polygon,)): x, y = np.asarray(obj.points).T r = regions.PolygonPixelRegion(vertices=regions.PixCoord(x=x, y=y)) elif isinstance(obj, (dc.Annulus,)) and obj.atype == 'circle': rin = obj.radius rout = rin + obj.width r = regions.CircleAnnulusPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), inner_radius=rin, outer_radius=rout) elif isinstance(obj, (dc.Annulus2R,)) and obj.atype == 'ellipse': r = regions.EllipseAnnulusPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), inner_width=obj.xradius * 2, inner_height=obj.yradius * 2, outer_width=obj.xradius * 2 + obj.xwidth * 2, outer_height=obj.yradius * 2 + obj.ywidth * 2, angle=obj.rot_deg * u.deg) elif isinstance(obj, (dc.Annulus2R,)) and obj.atype == 'box': r = regions.RectangleAnnulusPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y), inner_width=obj.xradius * 2, inner_height=obj.yradius * 2, outer_width=obj.xradius * 2 + obj.xwidth * 2, outer_height=obj.yradius * 2 + obj.ywidth * 2, angle=obj.rot_deg * u.deg) else: raise ValueError("Don't know how to convert this object") # Set visual styling attributes r.visual['color'] = obj.color if hasattr(obj, 'font'): r.visual['font'] = obj.font if obj.fontsize is not None: r.visual['fontsize'] = str(obj.fontsize) if hasattr(obj, 'linewidth'): r.visual['linewidth'] = obj.linewidth if hasattr(obj, 'fill'): r.visual['fill'] = obj.fill = r.visual.get('fill', False) # Limited support for other metadata r.meta['edit'] = 1 if obj.editable else 0 meta = obj.get_data() if meta is not None and meta.get('name', None) is not None: r.meta['name'] = meta.get('name') return r
def import_ds9_regions(ds9_file): """ Convenience function to read a ds9 file containing regions and return a list of matching Ginga canvas objects. Parameters ---------- ds9_file : str Path of a ds9 like regions file Returns ------- objs : list Returns a list of Ginga canvas objects that can be added to a Ginga canvas """ regs = regions.read_ds9(ds9_file) return [astropy_region_to_ginga_canvas_object(r) for r in regs]