Source code for metadata_tools.geometry_support.masks

################################################################################
# geometry_support/masks.py - Excluded-pixel mask construction.
#
# Config-free: depends only on oops and numpy so it can be unit-tested without
# the host geometry_config plugin.
################################################################################
from typing import Any

import numpy as np
import numpy.typing as npt
import oops


#===============================================================================
[docs] def construct_excluded_mask(backplane: Any, target: str, primary: str | None, mask_desc: tuple[str, str, str], *, blocker: str | None = None, ignore_shadows: bool = True) -> npt.NDArray[np.bool_] | bool: """Return a mask of excluded pixels for the given target. The mask is built from the specified target, maskers and shadowers. Parameters: backplane: The backplane defining the target surface. target: The name of the target surface. primary: Name of primary, e.g., "SATURN". mask_desc: A tuple (masker, shadower, face), where masker is a string identifying what surfaces can obscure the target, formed by concatenating "P" to let the planet obscure the target, "R" to let the rings obscure the target, and "M" to let the blocker body obscure the target; shadower is a string identifying what surfaces can shadow the target, formed by concatenating "P" to let the planet shadow the target, "R" to let the rings shadow the target, and "M" to let the blocker body shadow the target; and face is a string identifying which face(s) of the surface to include, where "D" includes only the day side of the target, "N" includes only the night side of the target, and "" includes both faces of the target. blocker: Optionally, the name of the body to use for any "M" codes that appear in the mask_desc. ignore_shadows: True to ignore any shadower or face constraints; default is False. Returns: Boolean bitmask containing the mask. """ # Do not let a body block itself if target == blocker: blocker = None # Generate the new mask, with True means included if isinstance(target, str): primary_name = target.split(':')[0] if not oops.Body.exists(primary_name): return True (masker, shadower, face) = mask_desc excluded = np.zeros(backplane.shape, dtype='bool') # Handle maskers if "R" in masker and primary == "SATURN": excluded |= backplane.where_in_back(target, "SATURN_MAIN_RINGS").vals if "P" in masker: excluded |= backplane.where_in_back(target, primary).vals if primary == "PLUTO": excluded |= backplane.where_in_back(target, "CHARON").vals if "M" in masker and blocker is not None: excluded |= backplane.where_in_back(target, blocker).vals if not ignore_shadows: # Handle shadowers if "R" in shadower and primary == "SATURN": excluded |= backplane.where_inside_shadow(target, "SATURN_MAIN_RINGS").vals if "P" in shadower: excluded |= backplane.where_inside_shadow(target, primary).vals if primary == "PLUTO": excluded |= backplane.where_inside_shadow(target, "CHARON").vals if "M" in shadower and blocker is not None: excluded |= backplane.where_inside_shadow(target, blocker).vals # Handle face selection if "D" in face: excluded |= backplane.where_antisunward(target).vals if "N" in face: excluded |= backplane.where_sunward(target).vals #!!!! # This function does not handle gridless backplanes properly. This # code fixes that, but the core problem should be fixed before this point. if np.any(excluded): return excluded return bool(np.all(excluded))