Source code for planetary_coverage.math.sphere

"""Auxiliary spherical formula."""

import numpy as np


def hav(theta):
    """Trigonometric half versine function.

    Parameters
    ----------
    theta: float or numpy.ndarray
        Angle in radians

    Returns
    -------
    float or numpy.ndarray
        Half versine value.

    """
    return .5 * (1 - np.cos(theta))


[docs]def hav_dist(lon_e_1, lat_1, lon_e_2, lat_2, r=1): """Calculate distance between 2 points on a sphere. Parameters ---------- lon_e_1: float or numpy.ndarray Point 1 east longitude (°). lat_1: float or numpy.ndarray Point 1 north latitude (°). lon_e_2: float or numpy.ndarray Point 2 east longitude (°). lat_2: float or numpy.ndarray Point 2 north latitude (°). r: float, optional Planet radius. Returns ------- float or numpy.ndarray Haversine distance between the 2 points. """ lambda_1, phi_1 = np.radians([lon_e_1, lat_1]) lambda_2, phi_2 = np.radians([lon_e_2, lat_2]) return 2 * r * np.arcsin(np.sqrt( hav(phi_2 - phi_1) + np.cos(phi_1) * np.cos(phi_2) * hav(lambda_2 - lambda_1) ))
[docs]def sph_pixel_scale(e, ifov, d, r): """Compute pixel scale in spherical geometry. Project both sides of the pixel iFOV on the sphere and measure its extend on the surface. If the pixel extend above the limb, a ``NaN`` will be return. .. image:: https://juigitlab.esac.esa.int/python/planetary-coverage/\ uploads/3427e7f9b8950cb9b672053a0d42d11c/sph_pixel_scale.png :alt: Pixel scale spherical geometry calculation. Parameters ---------- e: float, list or numpy.ndarray Local emission angle (in degrees). ifov: float Pixel instantaneous field of view (in radians). d: float, list or numpy.ndarray Distance from the observer to the target body center (in km). r: float Target body radius (in km). Returns ------- float Pixel scale on the surface (in km) and ``NaN`` if the pixel extend above the limb. Raises ------ ValueError If the observer distance is smaller than the target radius. """ if np.any(np.less_equal(d, r)): raise ValueError('Observer distance must be larger than the target radius.') # Off-nadir angles alpha = np.arcsin(np.divide(r * np.sin(np.radians(e)), d)) # Find all the valid pixels cond = alpha + ifov / 2 <= np.arcsin(np.divide(r, d)) # iFOV off-nadir angles alpha_2 = np.add(alpha, ifov / 2, where=cond, out=np.full_like(alpha, np.nan)) alpha_1 = alpha - ifov / 2 # Emergence angles e_1 = np.arcsin(np.multiply(d, np.sin(alpha_1) / r)) e_2 = np.arcsin(np.multiply(d, np.sin(alpha_2) / r)) return r * (e_2 - e_1 - ifov)