
    HR-eoQ                     v    d 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 g dZ	 dd
Z	 ddZddZddZddZdS )zB
This module contains functions for matching coordinate catalogs.
    N)units   )Angle)UnitSphericalRepresentation)SkyCoord)match_coordinates_3dmatch_coordinates_skysearch_around_3dsearch_around_sky	kdtree_3dc                 l   |j         st          |          dk     rt          d          t          ||          }t	          | t
                    r|                     |d          } n|                     |          } |j        j        j	        }| j        j
                            |          }|                    dt          j        |j                  dz  f          }t          j        |j                                                  rt          d          |                    |j        |          \  }}	|dk    r|dddf         }|	dddf         }	||	                             |           }
|	                    |j        dd                   |
|                    |j        dd                   |z  fS )	a?  
    Finds the nearest 3-dimensional matches of a coordinate or coordinates in
    a set of catalog coordinates.

    This finds the 3-dimensional closest neighbor, which is only different
    from the on-sky distance if ``distance`` is set in either ``matchcoord``
    or ``catalogcoord``.

    Parameters
    ----------
    matchcoord : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The coordinate(s) to match to the catalog.
    catalogcoord : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The base catalog in which to search for matches. Typically this will
        be a coordinate object that is an array (i.e.,
        ``catalogcoord.isscalar == False``)
    nthneighbor : int, optional
        Which closest neighbor to search for.  Typically ``1`` is desired here,
        as that is correct for matching one set of coordinates to another.
        The next likely use case is ``2``, for matching a coordinate catalog
        against *itself* (``1`` is inappropriate because each point will find
        itself as the closest match).
    storekdtree : bool or str, optional
        If a string, will store the KD-Tree used for the computation
        in the ``catalogcoord``, as in ``catalogcoord.cache`` with the
        provided name.  This dramatically speeds up subsequent calls with the
        same catalog. If False, the KD-Tree is discarded after use.

    Returns
    -------
    idx : int array
        Indices into ``catalogcoord`` to get the matched points for each
        ``matchcoord``. Shape matches ``matchcoord``.
    sep2d : `~astropy.coordinates.Angle`
        The on-sky separation between the closest match for each ``matchcoord``
        and the ``matchcoord``. Shape matches ``matchcoord``.
    dist3d : `~astropy.units.Quantity` ['length']
        The 3D distance between the closest match for each ``matchcoord`` and
        the ``matchcoord``. Shape matches ``matchcoord``.

    Notes
    -----
    This function requires `SciPy <https://www.scipy.org/>`_ to be installed
    or it will fail.
    r   CThe catalog for coordinate matching cannot be a scalar or length-0.Fmerge_attributes   z0Matching coordinates cannot contain NaN entries.N)isscalarlen
ValueError_get_cartesian_kdtree
isinstancer   transform_to	cartesianxunitxyztoreshapenpprodshapeisnanvalueanyqueryT
separation)
matchcoordcatalogcoordnthneighborstorekdtreekdtcatunitmatchxyzmatchflatxyzdistidxsep2ds              <lib/python3.11/site-packages/astropy/coordinates/matching.pyr   r      s   `  
L 1 1A 5 5Q
 
 	
  k
:
:C *h'' ;,,\E,RR

,,\::
 $&+G#'**733H##Q(?(?1(D$EFFL	x"##'')) MKLLL		,.+66ID#QAAArE{!!!R%j((44EHN122&''X^ABB'((72     
kdtree_skyc                 :   |j         st          |          dk     rt          d          t          | t                    r|                     |d          }n|                     |          }|j                            t                    }|	                    |          }|j                            t                    }|	                    |          }|j
                            ||          }t          ||||          \  }	}
}t          |j        t                    s5t          |j        t                    s||	                             |          }t          |t                    r|j
        |         |j
        |<   n|du r|j
        d         |j
        d<   |	|
|fS )a  
    Finds the nearest on-sky matches of a coordinate or coordinates in
    a set of catalog coordinates.

    This finds the on-sky closest neighbor, which is only different from the
    3-dimensional match if ``distance`` is set in either ``matchcoord``
    or ``catalogcoord``.

    Parameters
    ----------
    matchcoord : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The coordinate(s) to match to the catalog.
    catalogcoord : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The base catalog in which to search for matches. Typically this will
        be a coordinate object that is an array (i.e.,
        ``catalogcoord.isscalar == False``)
    nthneighbor : int, optional
        Which closest neighbor to search for.  Typically ``1`` is desired here,
        as that is correct for matching one set of coordinates to another.
        The next likely use case is ``2``, for matching a coordinate catalog
        against *itself* (``1`` is inappropriate because each point will find
        itself as the closest match).
    storekdtree : bool or str, optional
        If a string, will store the KD-Tree used for the computation
        in the ``catalogcoord`` in ``catalogcoord.cache`` with the
        provided name.  This dramatically speeds up subsequent calls with the
        same catalog. If False, the KD-Tree is discarded after use.

    Returns
    -------
    idx : int array
        Indices into ``catalogcoord`` to get the matched points for each
        ``matchcoord``. Shape matches ``matchcoord``.
    sep2d : `~astropy.coordinates.Angle`
        The on-sky separation between the closest match for each
        ``matchcoord`` and the ``matchcoord``. Shape matches ``matchcoord``.
    dist3d : `~astropy.units.Quantity` ['length']
        The 3D distance between the closest match for each ``matchcoord`` and
        the ``matchcoord``. Shape matches ``matchcoord``.  If either
        ``matchcoord`` or ``catalogcoord`` don't have a distance, this is the 3D
        distance on the unit sphere, rather than a true distance.

    Notes
    -----
    This function requires `SciPy <https://www.scipy.org/>`_ to be installed
    or it will fail.
    r   r   Fr   Tkdtree)r   r   r   r   r   r   datarepresent_asr   realize_framecachegetr   separation_3dstr)r(   r)   r*   r+   newmatchmatch_urepr
newmatch_u	cat_ureprnewcat_ur1   r2   sep3ds               r3   r	   r	   j   s   d  
L 1 1A 5 5Q
 
 	

 *h'' 9**<%*PP**<88 -,,-HIIK''44J!../JKKI)))44H
 $((kBBK,Hk; C 	<$&ABB:hm%@AA: S!//99 +s## @*2.*E;''			'/~h'?8$ur4   c                    |j         st          d          | j         s|j         rt          d          t          |           dk    st          |          dk    rmt          j        g t
                    t          j        g t
                    t          g t          j                  t          j	        g | j
        j                  fS t          ||          }|j        j        j        }|                     |          } t          | ||          }|                    |          }g }g }	t#          |                    ||                    D ]4\  }
}|D ],}|                    |
           |	                    |           -5t          j        |t
                    }t          j        |	t
                    }	|j        dk    r:t          g t          j                  }t          j	        g | j
        j                  }nB| |                             ||	                   }| |                             ||	                   }||	||fS )a	  
    Searches for pairs of points that are at least as close as a specified
    distance in 3D space.

    This is intended for use on coordinate objects with arrays of coordinates,
    not scalars.  For scalar coordinates, it is better to use the
    ``separation_3d`` methods.

    Parameters
    ----------
    coords1 : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The first set of coordinates, which will be searched for matches from
        ``coords2`` within ``seplimit``.  Cannot be a scalar coordinate.
    coords2 : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The second set of coordinates, which will be searched for matches from
        ``coords1`` within ``seplimit``.  Cannot be a scalar coordinate.
    distlimit : `~astropy.units.Quantity` ['length']
        The physical radius to search within.
    storekdtree : bool or str, optional
        If a string, will store the KD-Tree used in the search with the name
        ``storekdtree`` in ``coords2.cache``. This speeds up subsequent calls
        to this function. If False, the KD-Trees are not saved.

    Returns
    -------
    idx1 : int array
        Indices into ``coords1`` that matches to the corresponding element of
        ``idx2``. Shape matches ``idx2``.
    idx2 : int array
        Indices into ``coords2`` that matches to the corresponding element of
        ``idx1``. Shape matches ``idx1``.
    sep2d : `~astropy.coordinates.Angle`
        The on-sky separation between the coordinates. Shape matches ``idx1``
        and ``idx2``.
    dist3d : `~astropy.units.Quantity` ['length']
        The 3D distance between the coordinates. Shape matches ``idx1`` and
        ``idx2``. The unit is that of ``coords1``.

    Notes
    -----
    This function requires `SciPy <https://www.scipy.org/>`_
    to be installed or it will fail.

    If you are using this function to search in a catalog for matches around
    specific points, the convention is for ``coords2`` to be the catalog, and
    ``coords1`` are the points to search around.  While these operations are
    mathematically the same if ``coords1`` and ``coords2`` are flipped, some of
    the optimizations may work better if this convention is obeyed.

    In the current implementation, the return values are always sorted in the
    same order as the ``coords1`` (so ``idx1`` is in ascending order).  This is
    considered an implementation detail, though, so it could change in a future
    release.
    z.distlimit must be a scalar in search_around_3dzOne of the inputs to search_around_3d is a scalar. search_around_3d is intended for use with array coordinates, not scalars.  Instead, use ``coord1.separation_3d(coord2) < distlimit`` to find the coordinates near a scalar coordinate.r   dtype)	forceunit)r   r   r   r   arrayintr   udegQuantitydistancer   r   r   r   r   to_value	enumeratequery_ball_treeappendsizer'   r=   )coords1coords2	distlimitr+   kdt2cunitkdt1didxs1idxs2imatchesmatchd2dsd3dss                  r3   r
   r
      s1   n  KIJJJ 
7+ 
$
 
 	
 7||qCLLA-- HRs###HRs###"aeJr7+011	
 	
 !+66D$E
 ""7++G +GGGD 	5!!AEE 4 4T1 = =>>    
7 	  	 ELLOOOLL	  HU#&&&EHU#&&&EzQRz"g.344u~((88u~++GEN;;%t##r4   c                    |j         st          d          | j         s|j         rt          d          t          |           dk    st          |          dk    r|j        j        t
          j        k    rt
          j        }n| j        j        }t          j        g t                    t          j        g t                    t          g t
          j                  t          j        g |          fS |                     |          } | j                            t                     }|                     |          }t%          ||          }|r(|j                            |          r|j        |         }nV|j                            t                     }	|                    |	          }
t%          |
|          }|r||j        |du rdn|<   dt          j        t          |          dz            z  j        }g }g }t/          |                    ||                    D ]4\  }}|D ],}|                    |           |                    |           -5t          j        |t                    }t          j        |t                    }|j        dk    rc|j        j        t
          j        k    rt
          j        }n| j        j        }t          g t
          j                  }t          j        g |          }nn| |                             ||                   }	 | |                             ||                   }n*# t          $ r dt          j        |dz            z  }Y nw xY w||||fS )	a  
    Searches for pairs of points that have an angular separation at least as
    close as a specified angle.

    This is intended for use on coordinate objects with arrays of coordinates,
    not scalars.  For scalar coordinates, it is better to use the ``separation``
    methods.

    Parameters
    ----------
    coords1 : coordinate-like
        The first set of coordinates, which will be searched for matches from
        ``coords2`` within ``seplimit``. Cannot be a scalar coordinate.
    coords2 : coordinate-like
        The second set of coordinates, which will be searched for matches from
        ``coords1`` within ``seplimit``. Cannot be a scalar coordinate.
    seplimit : `~astropy.units.Quantity` ['angle']
        The on-sky separation to search within.
    storekdtree : bool or str, optional
        If a string, will store the KD-Tree used in the search with the name
        ``storekdtree`` in ``coords2.cache``. This speeds up subsequent calls
        to this function. If False, the KD-Trees are not saved.

    Returns
    -------
    idx1 : int array
        Indices into ``coords1`` that matches to the corresponding element of
        ``idx2``. Shape matches ``idx2``.
    idx2 : int array
        Indices into ``coords2`` that matches to the corresponding element of
        ``idx1``. Shape matches ``idx1``.
    sep2d : `~astropy.coordinates.Angle`
        The on-sky separation between the coordinates. Shape matches ``idx1``
        and ``idx2``.
    dist3d : `~astropy.units.Quantity` ['length']
        The 3D distance between the coordinates. Shape matches ``idx1``
        and ``idx2``; the unit is that of ``coords1``.
        If either ``coords1`` or ``coords2`` don't have a distance,
        this is the 3D distance on the unit sphere, rather than a
        physical distance.

    Notes
    -----
    This function requires `SciPy <https://www.scipy.org/>`_
    to be installed or it will fail.

    In the current implementation, the return values are always sorted in the
    same order as the ``coords1`` (so ``idx1`` is in ascending order).  This is
    considered an implementation detail, though, so it could change in a future
    release.
    z.seplimit must be a scalar in search_around_skyzOne of the inputs to search_around_sky is a scalar. search_around_sky is intended for use with array coordinates, not scalars.  Instead, use ``coord1.separation(coord2) < seplimit`` to find the coordinates near a scalar coordinate.r   rF   Tr7      g       @)r   r   r   rN   r   rK   dimensionless_unscaledr   rI   rJ   r   rL   rM   r   r8   r9   r   r:   r   r;   r<   sinr#   rP   rQ   rR   rS   r'   r=   )rT   rU   seplimitr+   distuniturepr1ucoords1rY   rW   urepr2ucoords2rr[   r\   r]   r^   r_   r`   ra   s                      r3   r   r   3  sY   h  KIJJJ 
7+ 
"
 
 	
 7||qCLLA-- A$<<</HH',HHRs###HRs###"aeJr8$$	
 	
 ""7++G \&&'BCCF$$V,,H ;77D Sw}((55 S}[) **+FGG((00$X{;; 	SNRGMkT&9&9(({K 
RVE(OOc)**	*1AEE 4 4T1 = =>>    
7 	  	 ELLOOOLL	  HU#&&&EHU#&&&EzQ A$<<</HH',HRz"h''u~((88	*5>//??DD 	* 	* 	* rvdSj)))DDD	*
 %t##s   !L9 9$M M r7   c                 j   ddl m} ddlm} 	 |j        }n"# t
          $ r  |d           |j        }Y nw xY w|du rd}t          |t                    rA| j	        
                    |d          }|#t          ||          st          d| d	          n9t          ||          r|}d}n$|sd}nt          d
t          |          z             ||| j        j        }n| j        j                            |          }|                    dt!          j        |j                  dz  f          }t!          j        |j                                                  rt-          d          	  ||j        j        dd          }n%# t          $ r  ||j        j                  }Y nw xY w|r
|| j	        |<   |S )a  
    This is a utility function to retrieve (and build/cache, if necessary)
    a 3D cartesian KD-Tree from various sorts of astropy coordinate objects.

    Parameters
    ----------
    coord : `~astropy.coordinates.BaseCoordinateFrame` or `~astropy.coordinates.SkyCoord`
        The coordinates to build the KD-Tree for.
    attrname_or_kdt : bool or str or KDTree
        If a string, will store the KD-Tree used for the computation in the
        ``coord``, in ``coord.cache`` with the provided name. If given as a
        KD-Tree, it will just be used directly.
    forceunit : unit or None
        If a unit, the cartesian coordinates will convert to that unit before
        being put in the KD-Tree.  If None, whatever unit it's already in
        will be used

    Returns
    -------
    kdt : `~scipy.spatial.cKDTree` or `~scipy.spatial.KDTree`
        The KD-Tree representing the 3D cartesian representation of the input
        coordinates.
    r   )warn)spatialzNC-based KD tree not found, falling back on (much slower) python implementationTr7   NzThe `attrname_or_kdt` "z" is not a scipy KD tree!z/Invalid `attrname_or_kdt` argument for KD-Tree:r   z/Catalog coordinates cannot contain NaN entries.F)compact_nodesbalanced_tree)warningsrn   scipyro   cKDTree	ExceptionKDTreer   r>   r;   r<   	TypeErrorr   r   r   r   r   r    r!   r"   r#   r$   r   r&   )	coordattrname_or_kdtrH   rn   ro   rv   r,   cartxyzflatxyzs	            r3   r   r     s3   0         $	
 	
 	
   $" /3'' 
kooot44?:c6#:#:?T/TTT   
OV	,	, 
 
=O@T@TT
 
 	
 {o)GGo),,Y77G//1bggm&<&<&A"BCC8GM""&&(( 	PNOOO	* &USSSCC 	* 	* 	* &))CCC	*
  +'*O$Js    55)F F$#F$)r   r   )r   r5   )r   )r5   )r7   N)__doc__numpyr   astropyr   rK    r   representationr   sky_coordinater   __all__r   r	   r
   r   r    r4   r3   <module>r      s
                    7 7 7 7 7 7 $ $ $ $ $ $   :EP P P Ph :F[ [ [ [|h$ h$ h$ h$V@$ @$ @$ @$FR R R R R Rr4   