
    IR-eE                     b   d Z ddlZddlmZ ddlZddl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 Zd Zd Z G d de          Z G d de          Z G d d          Z G d dee          Z G d dee          Z G d de          Z G d de          Z G 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)reduce)units)rotation_matrix   )Model)	Parameter)_to_orig_unit
_to_radian)RotateCelestial2NativeRotateNative2Celestial
Rotation2DEulerAngleRotationRotationSequence3DSphericalRotationSequencec                 @   g }t          | |          D ]i\  }}t          |t          j                  r|j        }|                                }|                    t          ||t          j                             jt          t          j        |d d d                   S )N)unit)zip
isinstanceuQuantityvalueitemappendr   radr   npmatmul)angles
axes_ordermatricesangleaxiss        :lib/python3.11/site-packages/astropy/modeling/rotations.py_create_matrixr$   +   s    H6:.. B BteQZ(( 	 KE

t!%@@@AAAA")Xddd^,,,    c                 L   t          j        |           } t          j        |          }t          j        |           t          j        |          z  }t          j        |          t          j        |           z  }t          j        |          }t          j        |||g          S N)r   deg2radcossinarray)alphadeltaxyzs        r#   spherical2cartesianr1   5   sw    JuEJuE
uu%A
uu%A
uA8Q1Ir%   c                     t          j        | |          }t          j        t          j        ||                     }t          j        t          j        ||                    }||fS r'   )r   hypotrad2degarctan2)r.   r/   r0   hr,   r-   s         r#   cartesian2sphericalr7   >   sQ    
AAJrz!Q''((EJrz!Q''((E%<r%   c                   n     e Zd ZdZdZdZdZdZ eg e	e
d          Zd
 fd	Zed             Zd	 Z x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setterdescriptionNc                    g d| _         t          |                              | j                   }|rt          d| d| j          d          || _        t          |          t          |          k    r0t          dt          |           dt          |           d          t                                          ||           d| _        d| _	        d S )	Nr.   r/   r0   Unrecognized axis label ; should be one of  zThe number of angles z! should match the number of axes .)name)
axesset
difference
ValueErrorr   lensuper__init___inputs_outputs)selfr   r   rE   unrecognized	__class__s        r#   rL   zRotationSequence3D.__init__d   s    #OO	:11$)<< 	X<XXDIXXX   %v;;#j//))9F 9 9&)*oo9 9 9   	d+++&'r%   c                 |    | j         j        ddd         dz  }|                     || j        ddd                   S )Inverse rotation.Nr   )r   )r   r   rQ   r   )rO   r   s     r#   inversezRotationSequence3D.inverseu   sA     "44R4(2-~~f21F~GGGr%   c                    |j         |j         k    s|j         |j         k    rt          d          |j         pd}t          j        |                                |                                |                                g          }t          j        t          |d         | j                  |          }|d         |d         |d         }}}|x|_         x|_         |_         |||fS )zJ
        Apply the rotation to a set of 3D Cartesian coordinates.
        ,Expected input arrays to have the same shaper   r   r      )shaperI   r   r+   flattendotr$   r   )rO   r.   r/   r0   r   
orig_shapeinarrresults           r#   evaluatezRotationSequence3D.evaluate{   s     7agAG!3!3KLLL W_
!))++qyy{{AIIKK@AAvay$/BBEJJ)VAYq	a1&000!'AG!Qwr%   r'   )__name__
__module____qualname____doc__standard_broadcasting
_separablen_inputs	n_outputsr   r	   r
   r   rL   propertyrT   r_   __classcell__rQ   s   @r#   r   r   E   s         $ "JHIYJ	  F( ( ( ( ( (" H H XH
      r%   r   c                   Z     e Zd ZdZd fd	Zed             Zed             Z fdZ x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                 z    d| _         d| _         t                      j        |f||d| d| _        d| _        d S )NrX   )r   rE   )lonlat)	_n_inputs
_n_outputsrK   rL   rM   rN   )rO   r   r   rE   kwargsrQ   s        r#   rL   z"SphericalRotationSequence.__init__   sI    LJTLLVLLL%&r%   c                     | j         S r'   )ro   rO   s    r#   rf   z"SphericalRotationSequence.n_inputs   s
    ~r%   c                     | j         S r'   )rp   rs   s    r#   rg   z#SphericalRotationSequence.n_outputs   s
    r%   c                     t          ||          \  }}}t                                          ||||          \  }}}	t          |||	          \  }}||fS r'   )r1   rK   r_   r7   )rO   rm   rn   r   r.   r/   r0   x1y1z1rQ   s             r#   r_   z"SphericalRotationSequence.evaluate   sY    %c3//1aWW%%aAv66
B&r2r22SCxr%   r'   )
r`   ra   rb   rc   rL   rh   rf   rg   r_   ri   rj   s   @r#   r   r      s         ' ' ' ' ' '   X   X        r%   r   c                   P    e Zd ZdZdZd ZdZdZed             Z	ed             Z
dS )_EulerRotationz7
    Base class which does the actual computation.
    Fc                 H   d }t          |t          j                  r/|                                }|                                }|j        }t          ||          }t          |||g|          }	t          j        |	|          }
t          |
 \  }}|||_        ||_        ||fS r'   )	r   r   ndarrayrZ   rY   r1   r$   r[   r7   )rO   r,   r-   phithetapsir   rY   inpmatrixr^   abs                r#   r_   z_EulerRotation.evaluate   s    eRZ(( 	 MMOOEMMOOEKE!%//eS 1:>>$$"F+1AGAG!tr%   Tc                 b    | j         d         t          j        | j         d         t          j        iS zInput units.r   r   inputsr   degrs   s    r#   input_unitsz_EulerRotation.input_units   #     At{1~qu==r%   c                 b    | j         d         t          j        | j         d         t          j        iS zOutput units.r   r   outputsr   r   rs   s    r#   return_unitsz_EulerRotation.return_units   #     QQ??r%   N)r`   ra   rb   rc   re   r_   _input_units_strict _input_units_allow_dimensionlessrh   r   r    r%   r#   rz   rz      s{          J   '+$> > X> @ @ X@ @ @r%   rz   c                        e Zd ZdZdZdZ edeed          Z	 edeed          Z
 edeed          Z fdZed	             Z fd
Z x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.
    rX   r   z*1st Euler angle (Quantity or value in deg)r:   z*2nd Euler angle (Quantity or value in deg)z*3rd Euler angle (Quantity or value in deg)c                    g d| _         t          |          dk    rt          d|           t          |                              | j                   }|rt          d| d| j                    || _        d |||fD             }t          |          rt          |          st          d           t                      j
        d
|||d| d	| _        d	| _        d S )Nr@   r9   z@Expected axes_order to be a character sequence of length 3, got rA   rB   c                 B    g | ]}t          |t          j                  S r   r   r   r   .0pars     r#   
<listcomp>z/EulerAngleRotation.__init__.<locals>.<listcomp>
  s$    GGGcjaj))GGGr%   >All parameters should be of the same type - float or Quantity.)r}   r~   r   )r,   r-   r   )rF   rJ   	TypeErrorrG   rH   rI   r   anyallrK   rL   rM   rN   )	rO   r}   r~   r   r   rq   rP   qsrQ   s	           r#   rL   zEulerAngleRotation.__init__   s   #OO	z??a$!$ $   :11$)<< 	W<WWDIWW   %GGc5#5FGGGr77 	3r77 	P   	AS3AA&AAA)*r%   c           	      t    |                      | j         | j         | j         | j        d d d                   S )Nr   )r}   r~   r   r   )rQ   r   r~   r}   r   rs   s    r#   rT   zEulerAngleRotation.inverse  sB    ~~	:+	ttt,	  
 
 	
r%   c                 h    t                                          |||||| j                  \  }}||fS r'   )rK   r_   r   )	rO   r,   r-   r}   r~   r   r   r   rQ   s	           r#   r_   zEulerAngleRotation.evaluate  s3    wwuc5#tOO1!tr%   )r`   ra   rb   rc   rf   rg   r   r	   r
   r}   r~   r   rL   rh   rT   r_   ri   rj   s   @r#   r   r      s         " HI
)@	  C I@	  E )@	  C+ + + + +. 
 
 X
        r%   r   c                        e Zd ZdZ edeed          Z edeed          Z edeed          Z	 fdZ
 fdZ xZS )	_SkyRotationzK
    Base class for RotateNative2Celestial and RotateCelestial2Native.
    r   Latituder:   
LongtitudezLongitude of a polec                     d |||fD             }t          |          rt          |          st          d           t                      j        |||fi | d| _        d S )Nc                 B    g | ]}t          |t          j                  S r   r   r   s     r#   r   z)_SkyRotation.__init__.<locals>.<listcomp>5  s$    JJJcjaj))JJJr%   r   zxz)r   r   r   rK   rL   r   )rO   rm   rn   lon_polerq   r   rQ   s         r#   rL   z_SkyRotation.__init__4  s}    JJc35IJJJr77 	3r77 	P   	c866v666r%   c                     t                                          |||||| j                  \  }}|dk     }t          |t          j                  r||xx         dz  cc<   n|dz  }||fS )Nr   ih  )rK   r_   r   r   r   r|   )
rO   r}   r~   rm   rn   r   r,   r-   maskrQ   s
            r#   	_evaluatez_SkyRotation._evaluate=  sr    ww''UChXXuqydBJ'' 	$KKK3KKKKSLEe|r%   )r`   ra   rb   rc   r   r	   r
   rm   rn   r   rL   r   ri   rj   s   @r#   r   r   "  s          )-

  C )-
  C y)	  H                 r%   r   c                   v     e Zd ZdZdZdZed             Zed             Z fdZ	 fdZ
ed             Z x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.
    rX   c                 b    | j         d         t          j        | j         d         t          j        iS r   r   rs   s    r#   r   z"RotateNative2Celestial.input_units^  r   r%   c                 b    | j         d         t          j        | j         d         t          j        iS r   r   rs   s    r#   r   z#RotateNative2Celestial.return_unitsc  r   r%   c                 \     t                      j        |||fi | d| _        d| _        d S )Nphi_Ntheta_Nalpha_Cdelta_CrK   rL   r   r   rO   rm   rn   r   rq   rQ   s        r#   rL   zRotateNative2Celestial.__init__h  s8    c866v666*-r%   c                 *   t          |t          j                  r|j        }|j        }|j        }|t          j        dz  z
  }t          j        dz  |z
   }t          j        dz  |z    }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.
        rX   r   r   r   r   r   pirK   r   )rO   r   r   rm   rn   r   r}   r~   r   r   r   rQ   s              r#   r_   zRotateNative2Celestial.evaluatem  s    & c1:&& 	&)C)C~H"%!)c/"	C  77,,UGS%MMr%   c                 B    t          | j        | j        | j                  S r'   )r   rm   rn   r   rs   s    r#   rT   zRotateNative2Celestial.inverse  s     &dh$-HHHr%   r`   ra   rb   rc   rf   rg   rh   r   r   rL   r_   rT   ri   rj   s   @r#   r   r   G  s         & HI> > X> @ @ X@. . . . .
         < I I XI I I I Ir%   r   c                   v     e Zd ZdZdZdZed             Zed             Z fdZ	 fdZ
ed             Z x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.
    rX   c                 b    | j         d         t          j        | j         d         t          j        iS r   r   rs   s    r#   r   z"RotateCelestial2Native.input_units  r   r%   c                 b    | j         d         t          j        | j         d         t          j        iS r   r   rs   s    r#   r   z#RotateCelestial2Native.return_units  r   r%   c                 \     t                      j        |||fi | d| _        d| _        d S )Nr   r   r   r   s        r#   rL   zRotateCelestial2Native.__init__  s:    c866v666 -+r%   c                 (   t          |t          j                  r|j        }|j        }|j        }t          j        dz  |z   }t          j        dz  |z
  }|t          j        dz  z
   }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.

        rX   r   )rO   r   r   rm   rn   r   r}   r~   r   r   r   rQ   s              r#   r_   zRotateCelestial2Native.evaluate  s    $ c1:&& 	&)C)C~Heai#o	C2519$%**7GS%MMwg~r%   c                 B    t          | j        | j        | j                  S r'   )r   rm   rn   r   rs   s    r#   rT   zRotateCelestial2Native.inverse  s    %dh$-HHHr%   r   rj   s   @r#   r   r     s         & HI> > X> @ @ X@, , , , ,    < I I XI I I I Ir%   r   c                        e Zd ZdZdZdZdZ edee	d          Z
e
f fd	Zed             Zed	             Zed
             Z x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).
    rX   Fg        z,Angle of rotation (Quantity or value in deg)r:   c                 Z     t                      j        dd|i| d| _        d| _        d S )Nr!   )r.   r/   r   )rK   rL   rM   rN   )rO   r!   rq   rQ   s      r#   rL   zRotation2D.__init__  s7    //u////!"r%   c                 :    |                      | j                   S )rS   r!   )rQ   r!   rs   s    r#   rT   zRotation2D.inverse  s     ~~TZK~000r%   c                    |j         |j         k    rt          d          t          |dd          }t          |dd          }|duo|du}||k    rC|r-|                    |          r|                    |          }|}nt          j        d          |j         pd}t          j        |	                                |	                                g          }t          |t
          j                  r|                    t
          j                  }t          j        |                     |          |          }	|	d         |	d         }}|x|_         |_         |r.t          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.

        rV   r   Nz"x and y must have compatible unitsrW   r   r   T)r   subok)rY   rI   getattris_equivalenttor   
UnitsErrorr   r+   rZ   r   r   to_valuer   r[   _compute_matrix)
clsr.   r/   r!   x_unity_unit	has_unitsr\   r]   r^   s
             r#   r_   zRotation2D.evaluate  s    7agKLLL FD))FD))$&=6+=	V IV11&99 IDDLLl#GHHH W_
!))++qyy{{344eQZ(( 	*NN15))E++E22E::ay&)1&&!' 	:afD9991:d< < <   !tr%   c                    t          j        |           s| d         } t          j        t          j        |           t          j        |            gt          j        |           t          j        |           ggt           j                  S )Nr   )dtype)r   isscalarr+   mathr)   r*   float64r   s    r#   r   zRotation2D._compute_matrix*  sp    {5!! 	!HExhuoo/048E??DHUOO2TU*
 
 
 	
r%   )r`   ra   rb   rc   rf   rg   re   r   r	   r
   r!   rL   rh   rT   classmethodr_   staticmethodr   ri   rj   s   @r#   r   r     s        	 	 HIJIB	  E # # # # # # #
 1 1 X1 ( ( [(T 
 
 \
 
 
 
 
r%   r   )rc   r   	functoolsr   numpyr   astropyr   r   $astropy.coordinates.matrix_utilitiesr   corer   
parametersr   utilsr	   r
   __all__r$   r1   r7   r   r   rz   r   r   r   r   r   r   r%   r#   <module>r      s|   $                  @ @ @ @ @ @       ! ! ! ! ! ! , , , , , , , ,  - - -    C C C C C C C CL" " " " " 2 " " "J"@ "@ "@ "@ "@ "@ "@ "@JJ J J J J J J JZ" " " " ">5 " " "JGI GI GI GI GI\ GI GI GITII II II II II\ II II IIXT
 T
 T
 T
 T
 T
 T
 T
 T
 T
r%   