B
    q\U                 @   s   d dl Zd dlmZ ddlmZmZmZm	Z	 ddgZ
ddddd	d
dddddgZdd Zdd Zd&ddZeggZeggZG dd dZeZG dd dZdd Zd'ddZdd Zdd	 Zd(dd
Zdd Zd d Zd!d" Zd)d$dZd*d%dZdS )+    N)units   )WCSWCSSUB_LONGITUDEWCSSUB_LATITUDEWCSSUB_CELESTIALwcs_to_celestial_framecelestial_frame_to_wcsadd_stokes_axis_to_wcsproj_plane_pixel_scalesproj_plane_pixel_areais_proj_plane_distortednon_celestial_pixel_scalesskycoord_to_pixelpixel_to_skycoordcustom_wcs_to_frame_mappingscustom_frame_to_wcs_mappingsc             C   sH   dd t | jjD }||d | |}d|jj|< d|jj|< |S )a  
    Add a new Stokes axis that is uncorrelated with any other axes.

    Parameters
    ----------
    wcs : `~astropy.wcs.WCS`
        The WCS to add to
    add_before_ind : int
        Index of the WCS to insert the new Stokes axis in front of.
        To add at the end, do add_before_ind = wcs.wcs.naxis
        The beginning is at position 0.

    Returns
    -------
    A new `~astropy.wcs.WCS` instance with an additional axis
    c             S   s   g | ]}|d  qS )r    ).0ir   r   0lib/python3.7/site-packages/astropy/wcs/utils.py
<listcomp>%   s    z*add_stokes_axis_to_wcs.<locals>.<listcomp>r   ZSTOKES)rangewcsnaxisinsertsubctypeZcname)r   Zadd_before_indZindsZnewwcsr   r   r   r
      s    
c             C   s  ddl m}m}m}m}m}m} ddlm} | j	j
dksD| j	jdkrHd S | j	j}t| j	jrdd }	n| j	j}	| j	j| j	j
 d d }
| j	j| j	j d d }|dkr|
dkr|dkr|	d krd	}n|	d
k rd}nd}|dkr|	d k	r||	dd}	||	d}n|dkr(|	d k	r||	dd}	||	d}n|dkrT|	d k	rH||	dd}	||	d}n\|d	krf| }nJ|
dkr|dkr| }n.|
dkr|dkr|| j	jpd d}nd }|S )Nr   )FK4FK4NoETermsFK5ICRSITRSGalactic)Time    zRA--zDEC-r!   g      @r   r    byear)format)equinoxzFK4-NO-EjyearGLONGLATTLONTLAT)obstime)astropy.coordinatesr   r   r    r!   r"   r#   Zastropy.timer$   r   ZlnglatradesysnpZisnanr*   r   dateobs)r   r   r   r    r!   r"   r#   r$   r3   r*   xcoordycoordframer   r   r   _wcs_to_celestial_frame_builtin-   sH     




r9   TANc             C   s  ddl m}m}m}m}m}m}m} tdd}	t	| |rd}
d}t	| |rTd|	j
_qt	| |rtd|	j
_| jj|	j
_qt	| |rd	|	j
_| jj|	j
_qt	| |rd
|	j
_| jj|	j
_qd S nBt	| |rd}
d}n.t	| |rd}
d}d|	j
_| jjj|	j
_nd S |
d | |d | g|	j
_|	S )Nr   )BaseRADecFramer   r   r    r!   r"   r#      )r   zRA--zDEC-r!   zFK4-NO-Er   r    r,   r-   r.   r/   r"   -)r1   r;   r   r   r    r!   r"   r#   r   
isinstancer   r3   r*   r(   r+   r0   ZutcZisotr5   r   )r8   
projectionr;   r   r   r    r!   r"   r#   r   r6   r7   r   r   r   _celestial_frame_to_wcs_builtind   s8    $








r@   c               @   s(   e Zd Zg fddZdd Zdd ZdS )r   c             C   s   t |dr|g}t| d S )N__call__)hasattrWCS_FRAME_MAPPINGSappend)selfmappingsr   r   r   __init__   s    
z%custom_wcs_to_frame_mappings.__init__c             C   s   d S )Nr   )rE   r   r   r   	__enter__   s    z&custom_wcs_to_frame_mappings.__enter__c             C   s   t   d S )N)rC   pop)rE   typevaluetbr   r   r   __exit__   s    z%custom_wcs_to_frame_mappings.__exit__N)__name__
__module____qualname__rG   rH   rM   r   r   r   r   r      s   c               @   s(   e Zd Zg fddZdd Zdd ZdS )r   c             C   s   t |dr|g}t| d S )NrA   )rB   FRAME_WCS_MAPPINGSrD   )rE   rF   r   r   r   rG      s    
z%custom_frame_to_wcs_mappings.__init__c             C   s   d S )Nr   )rE   r   r   r   rH      s    z&custom_frame_to_wcs_mappings.__enter__c             C   s   t   d S )N)rQ   rI   )rE   rJ   rK   rL   r   r   r   rM      s    z%custom_frame_to_wcs_mappings.__exit__N)rN   rO   rP   rG   rH   rM   r   r   r   r   r      s   c             C   s<   x.t D ]&}x |D ]}|| }|dk	r|S qW qW tddS )a  
    For a given WCS, return the coordinate frame that matches the celestial
    component of the WCS.

    Parameters
    ----------
    wcs : :class:`~astropy.wcs.WCS` instance
        The WCS to find the frame for

    Returns
    -------
    frame : :class:`~astropy.coordinates.baseframe.BaseCoordinateFrame` subclass instance
        An instance of a :class:`~astropy.coordinates.baseframe.BaseCoordinateFrame`
        subclass instance that best matches the specified WCS.

    Notes
    -----

    To extend this function to frames not defined in astropy.coordinates, you
    can write your own function which should take a :class:`~astropy.wcs.WCS`
    instance and should return either an instance of a frame, or `None` if no
    matching frame was found. You can register this function temporarily with::

        >>> from astropy.wcs.utils import wcs_to_celestial_frame, custom_wcs_to_frame_mappings
        >>> with custom_wcs_to_frame_mappings(my_function):
        ...     wcs_to_celestial_frame(...)

    NzMCould not determine celestial frame corresponding to the specified WCS object)rC   
ValueError)r   mapping_setfuncr8   r   r   r   r      s    

c             C   s@   x2t D ]*}x$|D ]}|| |d}|dk	r|S qW qW tddS )a
  
    For a given coordinate frame, return the corresponding WCS object.

    Note that the returned WCS object has only the elements corresponding to
    coordinate frames set (e.g. ctype, equinox, radesys).

    Parameters
    ----------
    frame : :class:`~astropy.coordinates.baseframe.BaseCoordinateFrame` subclass instance
        An instance of a :class:`~astropy.coordinates.baseframe.BaseCoordinateFrame`
        subclass instance for which to find the WCS
    projection : str
        Projection code to use in ctype, if applicable

    Returns
    -------
    wcs : :class:`~astropy.wcs.WCS` instance
        The corresponding WCS object

    Examples
    --------

    ::

        >>> from astropy.wcs.utils import celestial_frame_to_wcs
        >>> from astropy.coordinates import FK5
        >>> frame = FK5(equinox='J2010')
        >>> wcs = celestial_frame_to_wcs(frame)
        >>> wcs.to_header()
        WCSAXES =                    2 / Number of coordinate axes
        CRPIX1  =                  0.0 / Pixel coordinate of reference point
        CRPIX2  =                  0.0 / Pixel coordinate of reference point
        CDELT1  =                  1.0 / [deg] Coordinate increment at reference point
        CDELT2  =                  1.0 / [deg] Coordinate increment at reference point
        CUNIT1  = 'deg'                / Units of coordinate increment and value
        CUNIT2  = 'deg'                / Units of coordinate increment and value
        CTYPE1  = 'RA---TAN'           / Right ascension, gnomonic projection
        CTYPE2  = 'DEC--TAN'           / Declination, gnomonic projection
        CRVAL1  =                  0.0 / [deg] Coordinate value at reference point
        CRVAL2  =                  0.0 / [deg] Coordinate value at reference point
        LONPOLE =                180.0 / [deg] Native longitude of celestial pole
        LATPOLE =                  0.0 / [deg] Native latitude of celestial pole
        RADESYS = 'FK5'                / Equatorial coordinate system
        EQUINOX =               2010.0 / [yr] Equinox of equatorial coordinates


    Notes
    -----

    To extend this function to frames not defined in astropy.coordinates, you
    can write your own function which should take a
    :class:`~astropy.coordinates.baseframe.BaseCoordinateFrame` subclass
    instance and a projection (given as a string) and should return either a WCS
    instance, or `None` if the WCS could not be determined. You can register
    this function temporarily with::

        >>> from astropy.wcs.utils import celestial_frame_to_wcs, custom_frame_to_wcs_mappings
        >>> with custom_frame_to_wcs_mappings(my_function):
        ...     celestial_frame_to_wcs(...)

    )r?   NzHCould not determine WCS corresponding to the specified coordinate frame.)rQ   rR   )r8   r?   rS   rT   r   r   r   r   r	      s    >

c             C   s   t | jd jdtdS )a1  
    For a WCS returns pixel scales along each axis of the image pixel at
    the ``CRPIX`` location once it is projected onto the
    "plane of intermediate world coordinates" as defined in
    `Greisen & Calabretta 2002, A&A, 395, 1061 <http://adsabs.harvard.edu/abs/2002A%26A...395.1061G>`_.

    .. note::
        This function is concerned **only** about the transformation
        "image plane"->"projection plane" and **not** about the
        transformation "celestial sphere"->"projection plane"->"image plane".
        Therefore, this function ignores distortions arising due to
        non-linear nature of most projections.

    .. note::
        In order to compute the scales corresponding to celestial axes only,
        make sure that the input `~astropy.wcs.WCS` object contains
        celestial axes only, e.g., by passing in the
        `~astropy.wcs.WCS.celestial` WCS object.

    Parameters
    ----------
    wcs : `~astropy.wcs.WCS`
        A world coordinate system object.

    Returns
    -------
    scale : `~numpy.ndarray`
        A vector (`~numpy.ndarray`) of projection plane increments
        corresponding to each pixel side (axis). The units of the returned
        results are the same as the units of `~astropy.wcs.Wcsprm.cdelt`,
        `~astropy.wcs.Wcsprm.crval`, and `~astropy.wcs.Wcsprm.cd` for
        the celestial WCS and can be obtained by inquiring the value
        of `~astropy.wcs.Wcsprm.cunit` property of the input
        `~astropy.wcs.WCS` WCS object.

    See Also
    --------
    astropy.wcs.utils.proj_plane_pixel_area

    r<   r   )ZaxisZdtype)r4   Zsqrtpixel_scale_matrixsumfloat)r   r   r   r   r     s    )c             C   s,   | j j}|jdkrtdttj|S )aZ  
    For a **celestial** WCS (see `astropy.wcs.WCS.celestial`) returns pixel
    area of the image pixel at the ``CRPIX`` location once it is projected
    onto the "plane of intermediate world coordinates" as defined in
    `Greisen & Calabretta 2002, A&A, 395, 1061 <http://adsabs.harvard.edu/abs/2002A%26A...395.1061G>`_.

    .. note::
        This function is concerned **only** about the transformation
        "image plane"->"projection plane" and **not** about the
        transformation "celestial sphere"->"projection plane"->"image plane".
        Therefore, this function ignores distortions arising due to
        non-linear nature of most projections.

    .. note::
        In order to compute the area of pixels corresponding to celestial
        axes only, this function uses the `~astropy.wcs.WCS.celestial` WCS
        object of the input ``wcs``.  This is different from the
        `~astropy.wcs.utils.proj_plane_pixel_scales` function
        that computes the scales for the axes of the input WCS itself.

    Parameters
    ----------
    wcs : `~astropy.wcs.WCS`
        A world coordinate system object.

    Returns
    -------
    area : float
        Area (in the projection plane) of the pixel at ``CRPIX`` location.
        The units of the returned result are the same as the units of
        the `~astropy.wcs.Wcsprm.cdelt`, `~astropy.wcs.Wcsprm.crval`,
        and `~astropy.wcs.Wcsprm.cd` for the celestial WCS and can be
        obtained by inquiring the value of `~astropy.wcs.Wcsprm.cunit`
        property of the `~astropy.wcs.WCS.celestial` WCS object.

    Raises
    ------
    ValueError
        Pixel area is defined only for 2D pixels. Most likely the
        `~astropy.wcs.Wcsprm.cd` matrix of the `~astropy.wcs.WCS.celestial`
        WCS is not a square matrix of second order.

    Notes
    -----

    Depending on the application, square root of the pixel area can be used to
    represent a single pixel scale of an equivalent square pixel
    whose area is equal to the area of a generally non-square pixel.

    See Also
    --------
    astropy.wcs.utils.proj_plane_pixel_scales

    )r<   r<   z)Pixel area is defined only for 2D pixels.)	celestialrU   shaperR   r4   abslinalgdet)r   Zpsmr   r   r   r   H  s    7
h㈵>c             C   s   | j }t|j| pt|S )a  
    For a WCS returns `False` if square image (detector) pixels stay square
    when projected onto the "plane of intermediate world coordinates"
    as defined in
    `Greisen & Calabretta 2002, A&A, 395, 1061 <http://adsabs.harvard.edu/abs/2002A%26A...395.1061G>`_.
    It will return `True` if transformation from image (detector) coordinates
    to the focal plane coordinates is non-orthogonal or if WCS contains
    non-linear (e.g., SIP) distortions.

    .. note::
        Since this function is concerned **only** about the transformation
        "image plane"->"focal plane" and **not** about the transformation
        "celestial sphere"->"focal plane"->"image plane",
        this function ignores distortions arising due to non-linear nature
        of most projections.

    Let's denote by *C* either the original or the reconstructed
    (from ``PC`` and ``CDELT``) CD matrix. `is_proj_plane_distorted`
    verifies that the transformation from image (detector) coordinates
    to the focal plane coordinates is orthogonal using the following
    check:

    .. math::
        \left \| \frac{C \cdot C^{\mathrm{T}}}
        {| det(C)|} - I \right \|_{\mathrm{max}} < \epsilon .

    Parameters
    ----------
    wcs : `~astropy.wcs.WCS`
        World coordinate system object

    maxerr : float, optional
        Accuracy to which the CD matrix, **normalized** such
        that :math:`|det(CD)|=1`, should be close to being an
        orthogonal matrix as described in the above equation
        (see :math:`\epsilon`).

    Returns
    -------
    distorted : bool
        Returns `True` if focal (projection) plane is distorted and `False`
        otherwise.

    )rX   _is_cd_orthogonalrU   _has_distortion)r   maxerrZcwcsr   r   r   r     s    -c          	   C   s   | j }t|dkr"|d |d ks*tdttj| }|dkrLtdt| | j| }t	t|t
|d  }||k S )Nr<   r   r   z-CD (or PC) matrix must be a 2D square matrix.g        zCD (or PC) matrix is singular.)rY   lenrR   r4   rZ   r[   r\   dotTZamaxeye)Zcdr`   rY   ZpixareaIZcd_unitary_errr   r   r   r^     s    r^   c             C   sV   | j rtd| j}ttdtj|j  |drJtt	|t
j S tddS )aM  
    Calculate the pixel scale along each axis of a non-celestial WCS,
    for example one with mixed spectral and spatial axes.

    Parameters
    ----------
    inwcs : `~astropy.wcs.WCS`
        The world coordinate system object.

    Returns
    -------
    scale : `numpy.ndarray`
        The pixel scale along each axis.
    z4WCS is celestial, use celestial_pixel_scales insteadr   r   z8WCS is rotated, cannot determine consistent pixel scalesN)Zis_celestialrR   rU   r4   Zallcloseextractrd   rY   rZ   ZdiagonaluZdeg)ZinwcsZpccdr   r   r   r     s     c                s   t  fdddD S )zD
    `True` if contains any SIP or image distortion components.
    c             3   s   | ]}t  |d k	V  qd S )N)getattr)r   Z	dist_attr)r   r   r   	<genexpr>  s   z"_has_distortion.<locals>.<genexpr>)Zcpdis1Zcpdis2Zdet2im1Zdet2im2Zsip)any)r   r   )r   r   r_     s    r_   allc             C   s  t |r|jdkrtd|ttg}|jdkr:tdt|}t|j	j
d }t|j	j
d }| |} y | jj|}| jj|}W n0 tk
r   | jj|}| jj|}Y nX |dkr||j|j|\}	}
n*|dkr||j|j|\}	}
ntd|	|
fS )	a  
    Convert a set of SkyCoord coordinates into pixels.

    Parameters
    ----------
    coords : `~astropy.coordinates.SkyCoord`
        The coordinates to convert.
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use.
    origin : int
        Whether to return 0 or 1-based pixel coordinates.
    mode : 'all' or 'wcs'
        Whether to do the transformation including distortions (``'all'``) or
        only including only the core WCS transformation (``'wcs'``).

    Returns
    -------
    xp, yp : `numpy.ndarray`
        The pixel coordinates

    See Also
    --------
    astropy.coordinates.SkyCoord.from_pixel
    r<   z:Can only handle WCS with distortions for 2-dimensional WCSz&WCS should contain celestial componentr   r   rk   r   z$mode should be either 'all' or 'wcs')r_   r   rR   r   r   r   r   rg   Unitr   cunitZtransform_todatalontor2   AttributeErrorZ	sphericalZall_world2pixrK   Zwcs_world2pix)coordsr   originmoder8   Zxw_unitZyw_unitro   r2   xpypr   r   r   r     s*    


c             C   s   ddl m}m} |dkr|}t|r6|jdkr6td|ttg}|jdkrVtdt	|}t
|jjd }	t
|jjd }
|dkr|| ||\}}n$|d	kr|| ||\}}ntd
||	 }||
 }|||d}|||}|S )a  
    Convert a set of pixel coordinates into a `~astropy.coordinates.SkyCoord`
    coordinate.

    Parameters
    ----------
    xp, yp : float or `numpy.ndarray`
        The coordinates to convert.
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use.
    origin : int
        Whether to return 0 or 1-based pixel coordinates.
    mode : 'all' or 'wcs'
        Whether to do the transformation including distortions (``'all'``) or
        only including only the core WCS transformation (``'wcs'``).
    cls : class or None
        The class of object to create.  Should be a
        `~astropy.coordinates.SkyCoord` subclass.  If None, defaults to
        `~astropy.coordinates.SkyCoord`.

    Returns
    -------
    coords : Whatever ``cls`` is (a subclass of `~astropy.coordinates.SkyCoord`)
        The celestial coordinates

    See Also
    --------
    astropy.coordinates.SkyCoord.from_pixel
    r   )SkyCoordUnitSphericalRepresentationNr<   z:Can only handle WCS with distortions for 2-dimensional WCSz&WCS should contain celestial componentr   rk   r   z$mode should be either 'all' or 'wcs')ro   r2   )r1   rw   rx   r_   r   rR   r   r   r   r   rg   rl   r   rm   Zall_pix2worldZwcs_pix2worldZrealize_frame)ru   rv   r   rs   rt   clsrw   rx   r8   Zlon_unitZlat_unitro   r2   rn   rr   r   r   r   r   2  s*     
)r:   )r:   )r]   )r   rk   )r   rk   N)Znumpyr4   Zastropyr   rg   r   r   r   r   r   Z__doctest_skip____all__r
   r9   r@   rC   rQ   r   Zcustom_frame_mappingsr   r   r	   r   r   r   r^   r   r_   r   r   r   r   r   r   <module>   s6   7
)&
G,=
2
D