Skip to content

redplanet.analysis.radial_profile.get_concentric_ring_coords

get_concentric_ring_coords(
    lon: float,
    lat: float,
    radius_km: float,
    dist_btwn_rings_km: float = Ellipsis,
    num_rings: int = None,
    dist_btwn_points_km: float = 5,
) -> tuple[numpy.ndarray, tuple[numpy.ndarray]]

Generate concentric ring coordinates around a central point.

This function computes the radii for a series of concentric rings centered at the specified location and then generates (longitude, latitude) coordinates for equally spaced points along each ring. The rings can be defined by either a fixed distance between rings or by specifying the total number of rings to generate.

Parameters:

Name Type Description Default
lon float

Longitude coordinate of the center of the circle, in range [-180, 360].

required
lat float

Latitude coordinate of the center of the circle, in range [-90, 90].

required
radius_km float

Radius (in kilometers) of the largest/outermost ring.

required
dist_btwn_rings_km float

Distance (in kilometers) between consecutive rings. You can't provide both this and num_rings. Default is 5 km.

Ellipsis
num_rings int

Total number of rings to generate. You can't provide both this and dist_btwn_rings_km. Default is None.

None
dist_btwn_points_km float

Desired spacing (in kilometers) between adjacent points on each ring. Default is 5.

5

Returns:

Name Type Description
ring_radius_km__per_ring np.ndarray

Ring radii (in kilometers) for each ring.

ring_coords__per_ring tuple[np.ndarray]

A tuple of 2D numpy arrays, each containing the (longitude, latitude) coordinates of points on the corresponding ring (shape is (num_points,2)).

Raises:

Type Description
ValueError

If either both or neither of dist_btwn_rings_km and num_rings are specified.

Notes

For examples, see "Tutorials & Guides" on the RedPlanet documentation website.

Source code in src/redplanet/analysis/radial_profile.py
def get_concentric_ring_coords(
    lon                 : float,
    lat                 : float,
    radius_km           : float,
    dist_btwn_rings_km  : float = ...,
    num_rings           : int   = None,
    dist_btwn_points_km : float = 5,
) -> tuple[ np.ndarray, tuple[np.ndarray] ]:
    """
    Generate concentric ring coordinates around a central point.

    This function computes the radii for a series of concentric rings centered at the specified location and then generates (longitude, latitude) coordinates for equally spaced points along each ring. The rings can be defined by either a fixed distance between rings or by specifying the total number of rings to generate.

    Parameters
    ----------
    lon : float
        Longitude coordinate of the center of the circle, in range [-180, 360].
    lat : float
        Latitude coordinate of the center of the circle, in range [-90, 90].
    radius_km : float
        Radius (in kilometers) of the largest/outermost ring.
    dist_btwn_rings_km : float, optional
        Distance (in kilometers) between consecutive rings. You can't provide both this and `num_rings`. Default is 5 km.
    num_rings : int, optional
        Total number of rings to generate. You can't provide both this and `dist_btwn_rings_km`. Default is None.
    dist_btwn_points_km : float, optional
        Desired spacing (in kilometers) between adjacent points on each ring. Default is 5.

    Returns
    -------
    ring_radius_km__per_ring : np.ndarray
        Ring radii (in kilometers) for each ring.
    ring_coords__per_ring : tuple[np.ndarray]
        A tuple of 2D numpy arrays, each containing the (longitude, latitude) coordinates of points on the corresponding ring (shape is `(num_points,2)`).

    Raises
    ------
    ValueError
        If either both or neither of `dist_btwn_rings_km` and `num_rings` are specified.

    Notes
    -----
    For examples, see ["Tutorials & Guides"](/redplanet/tutorials/){target="_blank"} on the RedPlanet documentation website.
    """

    ## Input validation and defaults — after this, we're guaranteed one of `dist_btwn_rings_km` or `num_rings` will be a float and the other will be None.
    if (dist_btwn_rings_km is not ...) and (num_rings is not None):
        raise ValueError('Cannot provide both `dist_btwn_rings_km` and `num_rings` — provide only one or neither.')

    if num_rings:
        dist_btwn_rings_km = None
    else:
        dist_btwn_rings_km = 5

    ## Get radii for a series of concentric rings, starting at the center and going up to a distance of `radius_km`.
    if dist_btwn_rings_km:
        ring_radius_km__per_ring = np.arange(0, radius_km, dist_btwn_rings_km)
    else:
        ring_radius_km__per_ring = np.linspace(0, radius_km, num_rings)

    ## Calculate the number of points that can fit in each ring such that each point is `dist_btwn_points_km` away from its neighbors.
    ## Given a circle with radius `r`, the number of points you could fit around the circumference (`2*pi*r`) such that every point is distance `x` from its neighbors is given by `2*pi*r/x`.
    num_points__per_ring = np.ceil(2 * np.pi * ring_radius_km__per_ring / dist_btwn_points_km).astype(int)
    ## note: we're using ceil here to ensure atleast 1 point per ring :)    (i think???)

    ## Enforce a minimum number of points per ring
    min_num_points = 10
    num_points__per_ring[num_points__per_ring < min_num_points] = min_num_points

    ## Generate (lon,lat) coordinates for each point on each ring.
    ring_coords__per_ring = []
    for (ring_radius_km, num_points) in zip(ring_radius_km__per_ring, num_points__per_ring):
        ## Generate the (lon,lat) coordinates of `num_points` points around the circle of radius `ring_radius_km`.
        ring_coords = geodesy.make_circle(
            lon       = lon,
            lat       = lat,
            radius    = ring_radius_km * 1e3,
            n_samples = num_points,
            endpoint  = False,
        )
        ring_coords__per_ring.append(ring_coords)

    return (
        ring_radius_km__per_ring,
        tuple(ring_coords__per_ring)
    )