a
    ߙfbWD                     @   s   d Z ddlZddlZddlmZmZ ddlmZ	 ddl
mZ ddlmZ ddlmZmZ g d	Zd
d Zdd Zdd ZG dd deZG dd deZG dd dZG dd deeZG dd deeZG dd deZG dd deZG dd deZdS ) a  
Implements rotations, including spherical rotations as defined in WCS Paper II
[1]_

`RotateNative2Celestial` and `RotateCelestial2Native` follow the convention in
WCS Paper II to rotate to/from a native sphere and the celestial sphere.

The implementation uses `EulerAngleRotation`. The model parameters are
three angles: the longitude (``lon``) and latitude (``lat``) of the fiducial point
in the celestial system (``CRVAL`` keywords in FITS), and the longitude of the celestial
pole in the native system (``lon_pole``). The Euler angles are ``lon+90``, ``90-lat``
and ``-(lon_pole-90)``.


References
----------
.. [1] Calabretta, M.R., Greisen, E.W., 2002, A&A, 395, 1077 (Paper II)
    N)rotation_matrixmatrix_product)units   )Model)	Parameter)
_to_radian_to_orig_unit)RotateCelestial2NativeRotateNative2Celestial
Rotation2DEulerAngleRotationRotationSequence3DSphericalRotationSequencec                 C   s^   g }t | |D ]8\}}t|tjr(|j}| }|t||tjd qt	|d d d  }|S )Nunit)
zip
isinstanceuQuantityvalueitemappendr   radr   )angles
axes_orderZmatricesangleZaxisresult r   9lib/python3.9/site-packages/astropy/modeling/rotations.py_create_matrix%   s    r!   c                 C   sV   t | } t |}t | t | }t |t |  }t |}t |||gS N)npZdeg2radcossinarray)alphadeltaxyzr   r   r    spherical2cartesian0   s    


r,   c                 C   s8   t | |}t t || }t t ||}||fS r"   )r#   ZhypotZrad2degZarctan2)r)   r*   r+   hr'   r(   r   r   r    cartesian2spherical9   s    r.   c                       sV   e Zd ZdZdZdZdZdZeg e	e
ddZd fdd	Zed	d
 Zdd Z  ZS )r   a  
    Perform a series of rotations about different axis in 3D space.

    Positive angles represent a counter-clockwise rotation.

    Parameters
    ----------
    angles : array-like
        Angles of rotation in deg in the order of axes_order.
    axes_order : str
        A sequence of 'x', 'y', 'z' corresponding to axis of rotation.

    Examples
    --------
    >>> model = RotationSequence3D([1.1, 2.1, 3.1, 4.1], axes_order='xyzx')

    F   z4Angles of rotation in deg in the order of axes_orderdefaultgettersetterZdescriptionNc                    s~   g d| _ t|| j }|r0td|| j || _t|t|kr^tdt|t|t j||d d| _	d| _
d S )Nr)   r*   r+   z2Unrecognized axis label {0}; should be one of {1} z=The number of angles {0} should match the number of axes {1}.)name)axesset
difference
ValueErrorformatr   lensuper__init___inputs_outputs)selfr   r   r5   unrecognized	__class__r   r    r=   Y   s    
zRotationSequence3D.__init__c                 C   s0   | j jddd d }| j|| jddd dS )Inverse rotation.Nr   )r   )r   r   rC   r   )r@   r   r   r   r    inverseh   s    zRotationSequence3D.inversec                 C   s   |j |j ks|j |j kr td|j p(d}t| | | g}tt|d | j|}|d |d |d   }}}| |_  |_ |_ |||fS )zJ
        Apply the rotation to a set of 3D Cartesian coordinates.
        ,Expected input arrays to have the same shaper   r   r      )shaper9   r#   r&   flattendotr!   r   )r@   r)   r*   r+   r   
orig_shapeinarrr   r   r   r    evaluaten   s    
zRotationSequence3D.evaluate)N)__name__
__module____qualname____doc__Zstandard_broadcasting
_separablen_inputs	n_outputsr   r	   r   r   r=   propertyrE   rN   __classcell__r   r   rB   r    r   @   s   
r   c                       sF   e Zd ZdZd fdd	Zedd Zedd Z fd	d
Z  Z	S )r   a\  
    Perform a sequence of rotations about arbitrary number of axes
    in spherical coordinates.

    Parameters
    ----------
    angles : list
        A sequence of angles (in deg).
    axes_order : str
        A sequence of characters ('x', 'y', or 'z') corresponding to the
        axis of rotation and matching the order in ``angles``.

    Nc                    s6   d| _ d| _t j|f||d| d| _d| _d S )NrH   )r   r5   )lonlat)	_n_inputs
_n_outputsr<   r=   r>   r?   )r@   r   r   r5   kwargsrB   r   r    r=      s
    z"SphericalRotationSequence.__init__c                 C   s   | j S r"   )rZ   r@   r   r   r    rT      s    z"SphericalRotationSequence.n_inputsc                 C   s   | j S r"   )r[   r]   r   r   r    rU      s    z#SphericalRotationSequence.n_outputsc           
         s@   t ||\}}}t ||||\}}}	t|||	\}}||fS r"   )r,   r<   rN   r.   )
r@   rX   rY   r   r)   r*   r+   Zx1Zy1Zz1rB   r   r    rN      s    z"SphericalRotationSequence.evaluate)N)
rO   rP   rQ   rR   r=   rV   rT   rU   rN   rW   r   r   rB   r    r   ~   s   

r   c                   @   s<   e Zd ZdZdZdd ZdZdZedd Z	edd	 Z
d
S )_EulerRotationz7
    Base class which does the actual computation.
    Fc                 C   st   d }t |tjr&| }| }|j}t||}t|||g|}	t|	|}
t|
 \}}|d url||_||_||fS r"   )	r   r#   ndarrayrJ   rI   r,   r!   rK   r.   )r@   r'   r(   phithetapsir   rI   ZinpZmatrixr   abr   r   r    rN      s    
z_EulerRotation.evaluateTc                 C   s   | j d tj| j d tjiS z Input units. r   r   inputsr   degr]   r   r   r    input_units   s    z_EulerRotation.input_unitsc                 C   s   | j d tj| j d tjiS z Output units. r   r   outputsr   rh   r]   r   r   r    return_units   s    z_EulerRotation.return_unitsN)rO   rP   rQ   rR   rS   rN   Z_input_units_strictZ _input_units_allow_dimensionlessrV   ri   rm   r   r   r   r    r^      s   
r^   c                       sp   e Zd ZdZdZdZedeeddZ	edeeddZ
edeeddZ fdd	Zed
d Z fddZ  ZS )r   a1  
    Implements Euler angle intrinsic rotations.

    Rotates one coordinate system into another (fixed) coordinate system.
    All coordinate systems are right-handed. The sign of the angles is
    determined by the right-hand rule..

    Parameters
    ----------
    phi, theta, psi : float or `~astropy.units.Quantity` ['angle']
        "proper" Euler angles in deg.
        If floats, they should be in deg.
    axes_order : str
        A 3 character string, a combination of 'x', 'y' and 'z',
        where each character denotes an axis in 3D space.
    rH   r   z*1st Euler angle (Quantity or value in deg)r0   z*2nd Euler angle (Quantity or value in deg)z*3rd Euler angle (Quantity or value in deg)c                    s   g d| _ t|dkr$td|t|| j }|rJtd|| j || _dd |||fD }t|r|t	|s|tdt
 jf |||d| d	| _d	| _d S )
Nr4   r/   zBExpected axes_order to be a character sequence of length 3, got {}z0Unrecognized axis label {}; should be one of {} c                 S   s   g | ]}t |tjqS r   r   r   r   .0Zparr   r   r    
<listcomp>       z/EulerAngleRotation.__init__.<locals>.<listcomp>>All parameters should be of the same type - float or Quantity.)r`   ra   rb   )r'   r(   )r6   r;   	TypeErrorr:   r7   r8   r9   r   anyallr<   r=   r>   r?   )r@   r`   ra   rb   r   r\   rA   qsrB   r   r    r=      s&    
zEulerAngleRotation.__init__c                 C   s*   | j | j | j | j | jd d d dS )Nr   )r`   ra   rb   r   )rC   rb   ra   r`   r   r]   r   r   r    rE      s
    
zEulerAngleRotation.inversec                    s$   t  |||||| j\}}||fS r"   )r<   rN   r   )r@   r'   r(   r`   ra   rb   rc   rd   rB   r   r    rN      s    zEulerAngleRotation.evaluate)rO   rP   rQ   rR   rT   rU   r   r	   r   r`   ra   rb   r=   rV   rE   rN   rW   r   r   rB   r    r      s    
r   c                       s\   e Zd ZdZedeeddZedeeddZedeeddZ	 fddZ
 fd	d
Z  ZS )_SkyRotationzK
    Base class for RotateNative2Celestial and RotateCelestial2Native.
    r   ZLatituder0   Z
LongtitudezLongitude of a polec                    sN   dd |||fD }t |r,t|s,tdt j|||fi | d| _d S )Nc                 S   s   g | ]}t |tjqS r   rn   ro   r   r   r    rq     rr   z)_SkyRotation.__init__.<locals>.<listcomp>rs   Zzxz)ru   rv   rt   r<   r=   r   )r@   rX   rY   lon_poler\   rw   rB   r   r    r=     s
    z_SkyRotation.__init__c           	         sR   t  |||||| j\}}|dk }t|tjrB||  d7  < n|d7 }||fS )Nr   ih  )r<   rN   r   r   r#   r_   )	r@   r`   ra   rX   rY   ry   r'   r(   maskrB   r   r    	_evaluate  s    z_SkyRotation._evaluate)rO   rP   rQ   rR   r   r	   r   rX   rY   ry   r=   r{   rW   r   r   rB   r    rx     s   rx   c                       sX   e Zd ZdZdZdZedd Zedd Z fddZ	 fd	d
Z
edd Z  ZS )r   a~  
    Transform from Native to Celestial Spherical Coordinates.

    Parameters
    ----------
    lon : float or `~astropy.units.Quantity` ['angle']
        Celestial longitude of the fiducial point.
    lat : float or `~astropy.units.Quantity` ['angle']
        Celestial latitude of the fiducial point.
    lon_pole : float or `~astropy.units.Quantity` ['angle']
        Longitude of the celestial pole in the native system.

    Notes
    -----
    If ``lon``, ``lat`` and ``lon_pole`` are numerical values they
    should be in units of deg. Inputs are angles on the native sphere.
    Outputs are angles on the celestial sphere.
    rH   c                 C   s   | j d tj| j d tjiS re   rf   r]   r   r   r    ri   6  s    z"RotateNative2Celestial.input_unitsc                 C   s   | j d tj| j d tjiS rj   rk   r]   r   r   r    rm   <  s    z#RotateNative2Celestial.return_unitsc                    s(   t  j|||fi | d| _d| _d S )Nphi_Ntheta_Nalpha_Cdelta_Cr<   r=   rg   rl   r@   rX   rY   ry   r\   rB   r   r    r=   A  s    zRotateNative2Celestial.__init__c                    sl   t |tjr|j}|j}|j}|tjd  }tjd |  }tjd |  }t |||||\}	}
|	|
fS )a  
        Parameters
        ----------
        phi_N, theta_N : float or `~astropy.units.Quantity` ['angle']
            Angles in the Native coordinate system.
            it is assumed that numerical only inputs are in degrees.
            If float, assumed in degrees.
        lon, lat, lon_pole : float or `~astropy.units.Quantity` ['angle']
            Parameter values when the model was initialized.
            If float, assumed in degrees.

        Returns
        -------
        alpha_C, delta_C : float or `~astropy.units.Quantity` ['angle']
            Angles on the Celestial sphere.
            If float, in degrees.
        rH   r   r   r   r   r#   Zpir<   r{   )r@   r}   r~   rX   rY   ry   r`   ra   rb   r   r   rB   r   r    rN   F  s    zRotateNative2Celestial.evaluatec                 C   s   t | j| j| jS r"   )r
   rX   rY   ry   r]   r   r   r    rE   d  s    zRotateNative2Celestial.inverserO   rP   rQ   rR   rT   rU   rV   ri   rm   r=   rN   rE   rW   r   r   rB   r    r     s   

r   c                       sX   e Zd ZdZdZdZedd Zedd Z fddZ	 fd	d
Z
edd Z  ZS )r
   a~  
    Transform from Celestial to Native Spherical Coordinates.

    Parameters
    ----------
    lon : float or `~astropy.units.Quantity` ['angle']
        Celestial longitude of the fiducial point.
    lat : float or `~astropy.units.Quantity` ['angle']
        Celestial latitude of the fiducial point.
    lon_pole : float or `~astropy.units.Quantity` ['angle']
        Longitude of the celestial pole in the native system.

    Notes
    -----
    If ``lon``, ``lat`` and ``lon_pole`` are numerical values they should be
    in units of deg. Inputs are angles on the celestial sphere.
    Outputs are angles on the native sphere.
    rH   c                 C   s   | j d tj| j d tjiS re   rf   r]   r   r   r    ri     s    z"RotateCelestial2Native.input_unitsc                 C   s   | j d tj| j d tjiS rj   rk   r]   r   r   r    rm     s    z#RotateCelestial2Native.return_unitsc                    s(   t  j|||fi | d| _d| _d S )Nr   r|   r   r   rB   r   r    r=     s    zRotateCelestial2Native.__init__c                    sj   t |tjr|j}|j}|j}tjd | }tjd | }|tjd   }t |||||\}	}
|	|
fS )a;  
        Parameters
        ----------
        alpha_C, delta_C : float or `~astropy.units.Quantity` ['angle']
            Angles in the Celestial coordinate frame.
            If float, assumed in degrees.
        lon, lat, lon_pole : float or `~astropy.units.Quantity` ['angle']
            Parameter values when the model was initialized.
            If float, assumed in degrees.

        Returns
        -------
        phi_N, theta_N : float or `~astropy.units.Quantity` ['angle']
            Angles on the Native sphere.
            If float, in degrees.

        rH   r   )r@   r   r   rX   rY   ry   r`   ra   rb   r}   r~   rB   r   r    rN     s    zRotateCelestial2Native.evaluatec                 C   s   t | j| j| jS r"   )r   rX   rY   ry   r]   r   r   r    rE     s    zRotateCelestial2Native.inverser   r   r   rB   r    r
   j  s   

r
   c                       sd   e Zd ZdZdZdZdZedee	ddZ
e
f fdd	Zed	d
 Zedd Zedd Z  ZS )r   a  
    Perform a 2D rotation given an angle.

    Positive angles represent a counter-clockwise rotation and vice-versa.

    Parameters
    ----------
    angle : float or `~astropy.units.Quantity` ['angle']
        Angle of rotation (if float it should be in deg).
    rH   Fg        z,Angle of rotation (Quantity or value in deg)r0   c                    s&   t  jf d|i| d| _d| _d S )Nr   )r)   r*   )r<   r=   r>   r?   )r@   r   r\   rB   r   r    r=     s    zRotation2D.__init__c                 C   s   | j | j dS )rD   r   )rC   r   r]   r   r   r    rE     s    zRotation2D.inversec           
      C   s   |j |j krtdt|dd}t|dd}|duo:|du}||krl|rb||rb||}|}n
td|j ptd}t|	 |	 g}t
|tjr|tj}t| ||}	|	d |	d  }}| |_ |_ |rtj||dtj||dfS ||fS )	a  
        Rotate (x, y) about ``angle``.

        Parameters
        ----------
        x, y : array-like
            Input quantities
        angle : float or `~astropy.units.Quantity` ['angle']
            Angle of rotations.
            If float, assumed in degrees.

        rF   r   Nz"x and y must have compatible unitsrG   r   r   r   )rI   r9   getattrZis_equivalenttor   Z
UnitsErrorr#   r&   rJ   r   r   Zto_valuer   rK   _compute_matrix)
clsr)   r*   r   Zx_unitZy_unitZ	has_unitsrL   rM   r   r   r   r    rN     s(    


zRotation2D.evaluatec                 C   s6   t jt| t|  gt| t| ggt jdS )N)Zdtype)r#   r&   mathr$   r%   Zfloat64r   r   r   r    r     s
    zRotation2D._compute_matrix)rO   rP   rQ   rR   rT   rU   rS   r   r	   r   r   r=   rV   rE   classmethodrN   staticmethodr   rW   r   r   rB   r    r     s   


)r   )rR   r   Znumpyr#   Z$astropy.coordinates.matrix_utilitiesr   r   Zastropyr   r   corer   
parametersr   Zutilsr   r	   __all__r!   r,   r.   r   r   r^   r   rx   r   r
   r   r   r   r   r    <module>   s&   	>$';KM