Source code for balltree.coordinates

from __future__ import annotations

import numpy as np
from numpy.typing import ArrayLike, NDArray

__all__ = [
    "sgn",
    "angular_to_cylinder",
    "cylinder_to_angular",
    "angular_to_euclidean",
    "euclidean_to_angular",
    "angle_to_chorddist",
    "chorddist_to_angle",
]


def require_dim(data: NDArray, ndim: int) -> None:
    data_dim = data.shape[1]
    if data_dim != ndim:
        raise ValueError(f"input must have {ndim} dimensions but got {data_dim}")


def sgn(val: ArrayLike) -> NDArray:
    return 2.0 * (val >= 0.0) - 1.0  # 1.0 if (val >= 0.0) else -1.0


[docs] def angular_to_cylinder(radec: NDArray) -> NDArray: """ Convert angular coordinates in radian to cylindrical coordinates. ``radec`` can be of shape (2,) or (N, 2), result is guaranteed to be (N, 2). """ radec = np.atleast_2d(radec) require_dim(radec, 2) xy = np.empty_like(radec, dtype=np.float64) xy[:, 0] = radec[:, 0] xy[:, 1] = np.sin(radec[:, 1]) return xy
[docs] def cylinder_to_angular(xy: NDArray) -> NDArray: """ Convert cylindrical coordinates to angular coordinates in radian. ``xy`` can be of shape (2,) or (N, 2), result is guaranteed to be (N, 2). """ xy = np.atleast_2d(xy) require_dim(xy, 2) radec = np.empty_like(xy, dtype=np.float64) radec[:, 0] = xy[:, 0] radec[:, 1] = np.arcsin(xy[:, 1]) return radec
[docs] def angular_to_euclidean(radec: NDArray) -> NDArray: """ Convert angular coordinates in radian to Euclidean coordinates. ``radec`` can be of shape (2,) or (N, 2), result is guaranteed to be (N, 3). """ radec = np.atleast_2d(radec) require_dim(radec, 2) ra = radec[:, 0] dec = radec[:, 1] cos_dec = np.cos(dec) xyz = np.empty((len(ra), 3), dtype=np.float64) xyz[:, 0] = np.cos(ra) * cos_dec xyz[:, 1] = np.sin(ra) * cos_dec xyz[:, 2] = np.sin(dec) return xyz
[docs] def euclidean_to_angular(xyz: NDArray) -> NDArray: """ Convert Euclidean coordinates to angular coordinates in radian. ``xyz`` can be of shape (3,) or (N, 3), result is guaranteed to be (N, 2). """ xyz = np.atleast_2d(xyz) require_dim(xyz, 3) x = xyz[:, 0] y = xyz[:, 1] z = xyz[:, 2] r_d2 = np.sqrt(x * x + y * y) r_d3 = np.sqrt(x * x + y * y + z * z) radec = np.empty((len(x), 2), dtype=np.float64) x_normed = np.ones_like(x) # fallback for zero-division, arccos(1)=0.0 np.divide(x, r_d2, where=r_d2 > 0.0, out=x_normed) radec[:, 0] = np.arccos(x_normed) * sgn(y) % (2.0 * np.pi) radec[:, 1] = np.arcsin(z / r_d3) return radec
[docs] def angle_to_chorddist(angle: ArrayLike) -> NDArray: """Convert great circle distance (in radian) to chord distance.""" return 2.0 * np.sin(angle / 2.0, dtype=np.float64)
[docs] def chorddist_to_angle(chord: ArrayLike) -> NDArray: """Convert chord distance to great circle distance (in radian).""" return 2.0 * np.arcsin(chord / 2.0, dtype=np.float64)