========
Overview
========

pyregion is a python module to parse ds9 region files. It also support
ciao region files.

Please note that my main emphasis is to read in the regions files
generated by ds9 itself. It reads most of the region files created by
ds9. However, it may fail to read some of the user-created (or created
by other programs) region files, even if they can be successfully read
by ds9. Ruler, Compass and Projection type is ignored.


+----------------------------------------+----------------------------------------+
| ds9                                    | pyregion + matplotlib                  |
+========================================+========================================+
| .. image:: ../_static/region_ds9.jpg   | .. image:: ../_static/region_mpl.png   |
|   :width: 300px                        |   :width: 300px                        |
|   :target: ../static/region_ds9.jpg    |   :target: ../static/region_mpl.png    |
+----------------------------------------+----------------------------------------+


.. contents::
   :depth: 1
   :local:


Read Region Files
=================

*pyregion.open* takes the region name as an argument and returns a
ShapeList objects, which is basically a list of Shape objects. ::

    import pyregion
    region_name = "ds9.reg"
    r = pyregion.open(region_name)

You may use *pyregion.parse* if you have a string of region deniition. ::

    region = 'fk5;circle(290.96388,14.019167,843.31194")'
    r = pyregion.parse(region)

The shape object is a python representation of each region
definition. For example,::

    import pyregion

    region_string = """
    # Region file format: DS9 version 4.1
    # Filename: test01.fits
    global color=green dashlist=8 3 width=1 font="helvetica 10 normal" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1
    fk5
    circle(11:24:24.230,-59:15:02.20,18.5108") # color=cyan background
    box(11:24:39.213,-59:16:53.91,42.804",23.616",19.0384) # width=4
    """

    r = pyregion.parse(region_string)

And you have::

    >>> print r[0]
    Shape : circle ( HMS(11:24:24.230),DMS(-59:15:02.20),Ang(18.5108") )
    >>> print r[1]
    Shape : box ( HMS(11:24:39.213),DMS(-59:16:53.91),Ang(42.804"),Ang(23.616"),Number(19.0384) )

The shape object has following attributes,

* name : name of the shape. e.g., circle, box, etc.. ::

   >>> print r[0].name
   circle

* coord_format : coordinates format. e.g., "fk5", "image", "physical", etc... ::

   >>> print r[0].coord_format
   fk5

* coord_list : list of coordinates in *coord_format*. The coordinate
  value for sky coordinate is degree.  ::

   >>> print r[0].coord_list
   [171.10095833333332, -59.250611111111112, 0.0051418888888888886]

* comment : comment string associated with the shape (can be None) ::

   >>> print r[0].comment
   color=cyan background

* attr : attributes of the shape. This includes global attributes
  defined by the global command and local attributes defined in the
  comment. The first item is a list of key-only attributes without
  associated values (e.g., background..) and the second item is a
  dictionary of attributes of key-value pairs. ::

    >>> print r[0].attr[0]
    ['background']
    >>> print r[0].attr[1]
    {'color': 'cyan',
     'dash': '0 ',
     'dashlist': '8 3 ',
     'delete': '1 ',
     'edit': '1 ',
     'fixed': '0 ',
     'font': '"helvetica 10 normal"',
     'highlite': '1 ',
     'include': '1 ',
     'move': '1 ',
     'select': '1 ',
     'source': '1',
     'width': '1 '}


  Some attributes like "tag" allows multiple items and this is not
  currently supported (the last definition override previous ones).


The ShapeList class have a few method that could be
useful. *ShapeList.as_imagecoord* returns a new ShapeList instance
with the coordinates converted to the image coordinate. It
requires the Pyfits.Header instance.::

    import pyfits
    f = pyfits.open("t1.fits")
    r2 = pyregion.parse(region_string).as_imagecoord(f[0].header)

The return value is a new ShapeList instance, but the coordinate is
converted to the image coordinate. ::

    >>> print r2[0].coord_format
    image

    >>> print r2[0].coord_list
    [482.27721401429852, 472.76641383805912, 18.811792596807045]



Draw Regions with Matplotlib
============================

pyregion can help you draw the ds9 region with
matplotlib. *ShapeList.get_mpl_patches_texts* returns a list of
matplotlib.Artist ::

    r2 = pyregion.parse(region_string).as_imagecoord(f[0].header)
    patch_list, artist_list = r2.get_mpl_patches_texts()

The first item is a list of matplotlib.Patch, and the second one is
other kind of artists (usually Text). It is your responsibility to add
these to the axes. ::

    # ax is a mpl Axes object
    for p in patch_list:
        ax.add_patch(p)
    for t in artist_list:
        ax.add_artist(t)

.. plot:: figures/test_region_drawing.py

The (optional) argument of the *get_mpl_patches_texts* method is a
callable object that takes the shape object as an argument and returns
a dictionary object that will be used as a keyword arguments (e.g.,
colors and line width) for creating the mpl artists. By default, it
uses pyregion.mpl_helper.properties_func_default, which try to respect
the ds9 attributes. However, the colors (and other attributes) of some
complex shapes are not correctly handled as shown in above example,
and you need to manually adjust the associated attributes of patches.


.. plot:: figures/test_region_drawing2.py
   :include-source:



Use Regions for Spatial Filtering
=================================

*pyregion* includes some basic spatial filter support. ::

 >>> import pyregion._region_filter as filter
 >>> myfilter = filter.Circle(0, 0, 10) & filter.Box(15, 0, 10, 10)
 >>> myfilter.inside1(0, 0)
 0
 >>> myfilter.inside1(10, 0)
 1
 >>> myfilter.inside([0, 10], [0, 0])
 array([False,  True], dtype=bool)


ShapeList.get_filter method returns the filter from the parsed
region. The filter is meant to be used in the image coordinate, thus
you need to convert the region to the image coordinate before calling
get_filter. ::

    r2 = pyregion.parse(region_string).as_imagecoord(f[0].header)
    myfilter = r2.get_filter()
    myfilter.inside1(50, 30)

The returned filter has a *mask* method that creates an 2d mask. You
can create the mask directly from the ShapeList object. ::

    r2 = pyregion.parse(region_string)
    mymask = r2.get_mask(hdu=f[0])

It will creates an mask in the shape of the given hdu image (the mask
will be created after transforming the region to the image coordinate if
necessary).

.. plot:: figures/demo_filter_mask.py
   :include-source:


