"""Lidar module, containing the :class:`Lidar` class."""
import logging
from os import PathLike
from typing import Literal
import numpy.typing as npt
from numpy import ma
from cloudnetpy.datasource import DataSource
from cloudnetpy.utils import get_gap_ind, interpolate_2d_nearest
[docs]
class Lidar(DataSource):
    """Lidar class, child of DataSource.
    Args:
        full_path: Cloudnet Level 1 lidar netCDF file.
    """
    def __init__(self, full_path: str | PathLike) -> None:
        super().__init__(full_path)
        self.append_data(self.getvar("beta"), "beta")
        self._add_meta()
[docs]
    def interpolate_to_grid(
        self, time_new: npt.NDArray, height_new: npt.NDArray
    ) -> list[int]:
        """Interpolate beta using nearest neighbor."""
        max_height = 100  # m
        max_time = 1 / 60  # min -> fraction hour
        if self.height is None:
            msg = "Unable to interpolate lidar: no height information"
            raise RuntimeError(msg)
        # Interpolate beta to new grid but ignore profiles that are completely masked
        beta = self.data["beta"][:]
        indices = [ind for ind, b in enumerate(beta) if ma.all(b) is not ma.masked]
        beta_interp = interpolate_2d_nearest(
            self.time[indices],
            self.height,
            beta[indices, :],
            time_new,
            height_new,
        )
        # Mask data points that are too far from the original grid
        time_gap_ind = get_gap_ind(self.time[indices], time_new, max_time)
        height_gap_ind = get_gap_ind(self.height, height_new, max_height)
        self._mask_profiles(beta_interp, time_gap_ind, "time")
        self._mask_profiles(beta_interp, height_gap_ind, "height")
        self.data["beta"].data = beta_interp
        return time_gap_ind 
    @staticmethod
    def _mask_profiles(
        data: ma.MaskedArray, ind: list[int], dim: Literal["time", "height"]
    ) -> None:
        prefix = f"Unable to interpolate lidar for {len(ind)}"
        if dim == "time" and ind:
            logging.warning("%s time steps", prefix)
            data[ind, :] = ma.masked
        elif dim == "height" and ind:
            logging.warning("%s altitudes", prefix)
            data[:, ind] = ma.masked
    def _add_meta(self) -> None:
        self.append_data(float(self.getvar("wavelength")), "lidar_wavelength")
        self.append_data(0.5, "beta_error")
        self.append_data(3.0, "beta_bias")