B
    d                 @   sZ  d dl Zd dlmZ d dlmZ d dlZddlm	Z	m
Z
mZ ddgZddd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*d+ Z d,d- Z!d.d/ Z"d0d1 Z#d2d Z$d3d4d5dZ%d6d7 Z&d8d9 Z'dAd;dZ(dS )B    N)units)unbroadcast   )WCSWCSSUB_LONGITUDEWCSSUB_LATITUDE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_mappingspixel_to_pixellocal_partial_pixel_derivativesfit_wcs_from_pointsc             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
    -------
    `~astropy.wcs.WCS`
        A new `~astropy.wcs.WCS` instance with an additional axis
    c             S   s   g | ]}|d  qS )r    ).0ir   r   ^/work/yifan.wang/ringdown/master-ringdown-env/lib/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ctypecname)r   Zadd_before_indZindsZnewwcsr   r   r   r
      s    
c             C   s  ddl m}m}m}m}m}m}m} ddlm	} | j
jdksH| j
jdkrLd S | j
j}	t| j
jrhd }
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rZ|
d k	rN||
dd}
||
d}n^|	d	krl| }nL|dkr|dkr| }n0|dkr|dkr||| j
jpd d}nd }|S )Nr   )FK4FK4NoETermsFK5ICRSITRSGalacticSphericalRepresentation)Time    zRA--zDEC-r%   g      @r"   r$   byear)format)equinoxzFK4-NO-EjyearGLONGLATTLONTLAT)Zrepresentation_typeobstime)astropy.coordinatesr"   r#   r$   r%   r&   r'   r(   Zastropy.timer)   r   Zlnglatradesysnpisnanr/   r    dateobs)r   r"   r#   r$   r%   r&   r'   r(   r)   r8   r/   xcoordycoordframer   r   r   _wcs_to_celestial_frame_builtin1   sJ    $





r?   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$   r1   r2   r3   r4   r&   -)r6   rA   r"   r#   r$   r%   r&   r'   r   
isinstancer   r8   r/   r-   r0   r5   utcZisotr;   r    )r>   
projectionrA   r"   r#   r$   r%   r&   r'   r   r<   r=   r   r   r   _celestial_frame_to_wcs_builtinl   s8    $








rG   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   )rL   r   r   r   	__enter__   s    z&custom_wcs_to_frame_mappings.__enter__c             C   s   t   d S )N)rJ   pop)rL   typevaluetbr   r   r   __exit__   s    z%custom_wcs_to_frame_mappings.__exit__N)__name__
__module____qualname__rN   rO   rT   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 )NrH   )rI   FRAME_WCS_MAPPINGSrK   )rL   rM   r   r   r   rN      s    
z%custom_frame_to_wcs_mappings.__init__c             C   s   d S )Nr   )rL   r   r   r   rO      s    z&custom_frame_to_wcs_mappings.__enter__c             C   s   t   d S )N)rX   rP   )rL   rQ   rR   rS   r   r   r   rT      s    z%custom_frame_to_wcs_mappings.__exit__N)rU   rV   rW   rN   rO   rT   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)rJ   
ValueError)r   mapping_setfuncr>   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(...)

    )rF   NzHCould not determine WCS corresponding to the specified coordinate frame.)rX   rY   )r>   rF   rZ   r[   r   r   r   r   r	      s    >

c             C   s   t | jd jdtdS )a,  
    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 <https://ui.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 : 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

    rB   r   )axisdtype)r9   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 )a^  
    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 <https://ui.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

    )rB   rB   z)Pixel area is defined only for 2D pixels.)	celestialr_   shaperY   r9   abslinalgdet)r   Zpsmr   r   r   r   P  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 <https://ui.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.

    )rb   _is_cd_orthogonalr_   _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 )NrB   r   r   z-CD (or PC) matrix must be a 2D square matrix.g        zCD (or PC) matrix is singular.)rc   lenrY   r9   rd   re   rf   dotTZamaxeye)cdrj   rc   ZpixareaIZcd_unitary_errr   r   r   rh     s    rh   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_celestialrY   r_   r9   Zallcloseextractrn   rc   rd   Zdiagonalu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det2im2sip)any)r   r   )r   r   ri     s    ri   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
    rB   z:Can only handle WCS with distortions for 2-dimensional WCSz&WCS should contain celestial componentr   r   rx   r   z$mode should be either 'all' or 'wcs')ri   r   rY   r   r   r   r   rr   Unitr   cunittransform_todatalontor7   AttributeErrorZ	sphericalall_world2pixrR   Zwcs_world2pix)coordsr   originmoder>   Zxw_unitZyw_unitr}   r7   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 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 : `~astropy.coordinates.SkyCoord` subclass
        The celestial coordinates. Whatever ``cls`` type is.

    See Also
    --------
    astropy.coordinates.SkyCoord.from_pixel
    r   )SkyCoordUnitSphericalRepresentationNrB   z:Can only handle WCS with distortions for 2-dimensional WCSz&WCS should contain celestial componentr   rx   r   z$mode should be either 'all' or 'wcs')r}   r7   )r6   r   r   ri   r   rY   r   r   r   r   rr   ry   r   rz   Zall_pix2worldwcs_pix2worldZrealize_frame)r   r   r   r   r   clsr   r   r>   Zlon_unitZlat_unitr}   r7   r|   r   r   r   r   r   :  s*     
c             C   s(   g }x| D ]}||kr
| | q
W |S )zo
    Return a list of unique items in the list provided, preserving the order
    in which they are found.
    )rK   )itemsZ	new_itemsitemr   r   r   _unique_with_order_preserved  s
    
r   c                s   | j j}| j j | j j}tdd |D }tjt|| jft	d}x8t
| jD ]*}||| d }||  || O  < qNW  fdd|D }||fS )a(  
    Return a correlation matrix between the pixel coordinates and the
    high level world coordinates, along with the list of high level world
    coordinate classes.

    The shape of the matrix is ``(n_world, n_pix)``, where ``n_world`` is the
    number of high level world coordinates.
    c             S   s   g | ]}|d  qS )r   r   )r   cr   r   r   r     s    z6_pixel_to_world_correlation_matrix.<locals>.<listcomp>)r]   r   c                s   g | ]} | d  qS )r   r   )r   	component)all_classesr   r   r     s    )Zlow_level_wcsZworld_axis_object_componentsZworld_axis_object_classesaxis_correlation_matrixr   r9   zerosrk   pixel_n_dimboolr   world_n_dimindex)r   Zall_componentsr   
componentsmatrixZiworldZiworld_uniqueclassesr   )r   r   "_pixel_to_world_correlation_matrix  s    r   c             C   s   t | \}}t |\}}t|t|kr0tdd}g }xH|D ]@}||}	|	dkr^tdq>|	dkrnd}P q>||| q>W |r|| }n||krtdt|j|}
|
S )a  
    Correlation matrix between the input and output pixel coordinates for a
    pixel -> world -> pixel transformation specified by two WCS instances.

    The first WCS specified is the one used for the pixel -> world
    transformation and the second WCS specified is the one used for the world ->
    pixel transformation. The shape of the matrix is
    ``(n_pixel_out, n_pixel_in)``.
    z:The two WCS return a different number of world coordinatesTr   z6The world coordinate types of the two WCS do not matchr   FzHWorld coordinate order doesn't match and automatic matching is ambiguous)	r   rk   rY   countrK   r   r9   matmulrm   )wcs_inwcs_outZmatrix1Zclasses1Zmatrix2Zclasses2unique_matchmappingZclass1matchesr   r   r   r   "_pixel_to_pixel_correlation_matrix  s(    



r   c       
      C   s   g }g }xt | jd D ]}||kr&qtj| jd td}d||< d\}}xL||kr| dd|f jdd}| |ddf jdd}|t| }}qLW tt|d }tt|d }	|	| |
||	f qW |S )a  
    Given an axis correlation matrix from a WCS object, return information about
    the individual WCS that can be split out.

    The output is a list of tuples, where each tuple contains a list of
    pixel dimensions and a list of world dimensions that can be extracted to
    form a new WCS. For example, in the case of a spectral cube with the first
    two world coordinates being the celestial coordinates and the third
    coordinate being an uncorrelated spectral axis, the matrix would look like::

        array([[ True,  True, False],
               [ True,  True, False],
               [False, False,  True]])

    and this function will return ``[([0, 1], [0, 1]), ([2], [2])]``.
    r   )r]   T)r   r   N)r\   r   )r   rc   r9   r   r   rw   r`   listZnonzeroextendrK   )
r   Z
pixel_used
split_infoipixZpixel_includeZ
n_pix_prevZn_pixZworld_includeZpixel_indicesZworld_indicesr   r   r   _split_matrix  s"    

r   c             G   sF  t |d r6| j| }t|ttfs,|f}|j| S |d j}t| |}t	|}dg|j
 }x|D ]\}}	g }
xBt| j
D ]4}||kr|
t||  q||
|| jd  q|W t j|
 }
| j|
 }t|ttfs|f}|j| }|j
dkr|f}x2t|j
D ]$}||	krt || |||< qW qdW |j
dkrB|d S |S )a  
    Transform pixel coordinates in a dataset with a WCS to pixel coordinates
    in another dataset with a different WCS.

    This function is designed to efficiently deal with input pixel arrays that
    are broadcasted views of smaller arrays, and is compatible with any
    APE14-compliant WCS.

    Parameters
    ----------
    wcs_in : `~astropy.wcs.wcsapi.BaseHighLevelWCS`
        A WCS object for the original dataset which complies with the
        high-level shared APE 14 WCS API.
    wcs_out : `~astropy.wcs.wcsapi.BaseHighLevelWCS`
        A WCS object for the target dataset which complies with the
        high-level shared APE 14 WCS API.
    *inputs :
        Scalars or arrays giving the pixel coordinates to transform.
    r   Nr   )r9   ZisscalarZpixel_to_worldrD   tupler   Zworld_to_pixelrc   r   r   r   r   rK   r   ZflatZbroadcast_arraysZbroadcast_to)r   r   inputsZworld_outputsZoriginal_shaper   r   outputsZpixel_in_indicesZpixel_out_indicesZpixel_inputsr   Zpixel_outputsr   r   r   r     s4    








F)normalize_by_worldc      	      G   s   t |}t | j| }t | j| jf}xNt| jD ]@}| }||  d7  < t | j| }|| |dd|f< q8W |r||jddddt j	f  }|S )a  
    Return a matrix of shape ``(world_n_dim, pixel_n_dim)`` where each entry
    ``[i, j]`` is the partial derivative d(world_i)/d(pixel_j) at the requested
    pixel position.

    Parameters
    ----------
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to evaluate the derivatives for.
    *pixel : float
        The scalar pixel coordinates at which to evaluate the derivatives.
    normalize_by_world : bool
        If `True`, the matrix is normalized so that for each world entry
        the derivatives add up to 1.
    r   Nr   )r\   )
r9   arrayZpixel_to_world_valuesr   r   r   r   copyr`   Znewaxis)	r   r   ZpixelZ	pixel_refZ	world_refZderivativesr   Z	pixel_offZ	world_offr   r   r   r   E  s    
c             C   s   | dd }| dd }|d |d f|d |d ff|j _||j _|||d\}}	||	 }
|| }t|d dd }t|tt| |
f}|S )	a?  
    Objective function for fitting linear terms.

    Parameters
    ----------
    params : array
        6 element array. First 4 elements are PC matrix, last 2 are CRPIX.
    lon, lat: array
        Sky coordinates.
    x, y: array
        Pixel coordinates
    w_obj: `~astropy.wcs.WCS`
        WCS object
        r   r+      r   rB      g     f@g     v@)	r   ro   crpixr   r9   modconcatenatecosradians)paramsr}   r7   xyw_objro   r   Zlon2Zlat2Z
lat_residsZ
lon_residsresidsr   r   r   _linear_wcs_fiti  s    $r   c          	   C   sX  ddl m} | dd }	| dd d}
| ddt|  }| dt| d }|
|j_|	|j_i i  }}x<tt|D ],}|| |d||  < || |d||  < qzW ||	||||d	}|||\}}t	|
t
|| |	d  || |	d
  g\}}|||d\}}t	|jj||jjd  ||jjd
  f\}}t|| || f}|S )a2   Objective function for fitting SIP.

    Parameters
    ----------
    params : array
        Fittable parameters. First 4 elements are PC matrix, last 2 are CRPIX.
    lon, lat: array
        Sky coordinates.
    u, v: array
        Pixel coordinates
    w_obj: `~astropy.wcs.WCS`
        WCS object
    rB   )SIPr   r   )rB   rB   NZA_ZB_)r   Za_orderZb_ordera_coeffb_coeffr   )Zmodeling.modelsr   reshaperk   r   ro   r   r   r9   rl   r   r   r   )r   r}   r7   rr   vr   orderZcoeff_namesr   r   ZcdxZa_paramsZb_paramsr   r   r   rv   ZfuvZguvZxoZyor   r   r   r   r   r   _sip_fit  s&    

20r   centerc       "         s  ddl m} ddlm} ddlm} ddlm} | \}	}
y|jj	j
|jjj
 }}W n, tk
r~   |j}|j	j
|jj
 }}Y nX t|t|kr|dkrtdt|dk}|s|jdkstd	d
dddddddddddddddddddddd d!d"d#g}t|tkr0||kr td$d%|t|j|d&}nt|}d'|_d|_|j rj|jj|j_|jd( t|tdkrt|tkrtd)|	 |	  |
 |
  f\}}}}|d*krdntt!"||d*krdntt!"|f|_#d+d, }|r|| |j
 |  |j
 }||  |j
 | |j
 }|$|}|%|}|&||d- }|jj	j
|jjj
f|j_'|| d. || d. f|j_(nZ|)| |jj	j
|jjj
f|j_'|||jj'd  |	d |||jj'd  |
d f|j_(||kr
|d/ |d/  }}||kr&|d/ |d/  }}t!*|jj+ |jj(+ g}|t,||||	|
|ft!j- t!j- t!j- t!j- |d |d gt!j-t!j-t!j-t!j-|d |d ggd0}t!.|j/d1d2 |j_(t!.|j/dd1 0d3|j_|r| d4|jj1d kr
d5d6 |jj1D |j_1 fd7d6t2 d D }t!*t!.|jj(|jj+ t!3d-t4| f}|t5||||	|
| |f|d |d gt!j- gd1d-t4|    |d |d gt!j-gd1d-t4|    gd0}t6|j/d2d2t4|  t6|j/d2t4| d f}|j/d-d2 0d3|j_|j/dd- |j_(t!3 d  d f}t!3 d  d f} xZ|D ]R}!|d 7d|t|!d  t|!d- < |d 7d| t|!d  t|!d- < qPW ||| t!3 d  d ft!3 d  d f|jj(|_|S )8a  
    Given two matching sets of coordinates on detector and sky,
    compute the WCS.

    Fits a WCS object to matched set of input detector and sky coordinates.
    Optionally, a SIP can be fit to account for geometric
    distortion. Returns an `~astropy.wcs.WCS` object with the best fit
    parameters for mapping between input pixel and sky coordinates.

    The projection type (default 'TAN') can passed in as a string, one of
    the valid three-letter projection codes - or as a WCS object with
    projection keywords already set. Note that if an input WCS has any
    non-polynomial distortion, this will be applied and reflected in the
    fit terms and coefficients. Passing in a WCS object in this way essentially
    allows it to be refit based on the matched input coordinates and projection
    point, but take care when using this option as non-projection related
    keywords in the input might cause unexpected behavior.

    Notes
    -----
    - The fiducial point for the spherical projection can be set to 'center'
      to use the mean position of input sky coordinates, or as an
      `~astropy.coordinates.SkyCoord` object.
    - Units in all output WCS objects will always be in degrees.
    - If the coordinate frame differs between `~astropy.coordinates.SkyCoord`
      objects passed in for ``world_coords`` and ``proj_point``, the frame for
      ``world_coords``  will override as the frame for the output WCS.
    - If a WCS object is passed in to ``projection`` the CD/PC matrix will
      be used as an initial guess for the fit. If this is known to be
      significantly off and may throw off the fit, set to the identity matrix
      (for example, by doing wcs.wcs.pc = [(1., 0.,), (0., 1.)])

    Parameters
    ----------
    xy : (`numpy.ndarray`, `numpy.ndarray`) tuple
        x & y pixel coordinates.
    world_coords : `~astropy.coordinates.SkyCoord`
        Skycoord object with world coordinates.
    proj_point : 'center' or ~astropy.coordinates.SkyCoord`
        Defaults to 'center', in which the geometric center of input world
        coordinates will be used as the projection point. To specify an exact
        point for the projection, a Skycoord object with a coordinate pair can
        be passed in. For consistency, the units and frame of these coordinates
        will be transformed to match ``world_coords`` if they don't.
    projection : str or `~astropy.wcs.WCS`
        Three letter projection code, of any of standard projections defined
        in the FITS WCS standard. Optionally, a WCS object with projection
        keywords set may be passed in.
    sip_degree : None or int
        If set to a non-zero integer value, will fit SIP of degree
        ``sip_degree`` to model geometric distortion. Defaults to None, meaning
        no distortion corrections will be fit.

    Returns
    -------
    wcs : `~astropy.wcs.WCS`
        The best-fit WCS to the points given.
    r   )r   Nr   )Sip)least_squaresr   zfproj_point must be set to 'center', or an`~astropy.coordinates.SkyCoord` object with a pair of points.ZAZPZSZPr@   ZSTGZSINZARCZZEAZAIRZCYPZCEAZCARZMERZSFLZPARZMOLZAITZCOPZCOEZCODZCOOZBONZPCOZTSCZCSCZQSCZHPXZXPHzAMust specify valid projection code from list of supported types: z, )r>   rF   )g      ?g      ?pcz$sip_degree must be None, or integer.g        c             S   s   |t t |  S )N)r9   Zargminrd   )lpr   r   r   <lambda>.      z%fit_wcs_from_points.<locals>.<lambda>rB   g       @g      ?)argsZboundsr+   r   )rB   rB   z-SIPc             S   s   g | ]}|d  qS )z-SIPr   )r   r   r   r   r   r   T  s    z'fit_wcs_from_points.<locals>.<listcomp>c                sH   g | ]@}t  d  D ].}||  d  k r|| d kr| d| qqS )r   _)r   )r   r   j)degreer   r   r   V  s     )8r6   r   Zastropy.unitsr   r   r   Zscipy.optimizer   r|   r}   rs   r7   r   Zunit_sphericalrQ   rY   strsizeAssertionErrorjoinr	   r>   r   deepcopyZcdeltrv   Zhas_pcr   ro   __delattr__intminmaxr9   ceilZpixel_shapeZposition_angleZ
separationZdirectional_offset_byZcrvalr   r{   r   flattenr   infr   r   r   r    r   r   rk   r   r   rP   )"ZxyZworld_coordsZ
proj_pointrF   Z
sip_degreer   rr   r   r   r   r   r}   r7   Zunit_sphZuse_center_as_proj_pointZ
proj_codesr   ZxpminZxpmaxZypminZypmaxcloseZsc1Zsc2pasepZmidpoint_scp0fitZ
coef_namesZcoef_fitZa_valsZb_valsZ	coef_namer   )r   r   r     s    =


 $ 


 

&&(.
&, )r@   )r@   )rg   )r   rx   )r   rx   N)r   r@   N))numpyr9   Zastropyr   rr   Zastropy.utilsr   r   r   r   r   r   Z__doctest_skip____all__r
   r?   rG   rJ   rX   r   Zcustom_frame_mappingsr   r   r	   r   r   r   rh   r   ri   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   sP   ;
)&
G,=
2
D
K-(@$ 0 