asp_plot.sensors
================

.. py:module:: asp_plot.sensors

.. autoapi-nested-parse::

   Sensor-specific metadata readers for stereo scenes.

   This module isolates the *sensor-specific* work of discovering scene files and
   extracting per-scene metadata from the *sensor-agnostic* stereo-pair geometry
   math in :mod:`asp_plot.stereopair_metadata_parser`.

   The goal is flexibility: today only WorldView (and other DigitalGlobe-heritage)
   XML camera files are supported, but adding a new sensor (ASTER, HiRISE, etc.) is
   a matter of writing a new :class:`SensorMetadata` subclass and registering it in
   ``SENSORS`` — no changes to the pair-level geometry code are required.

   Each reader is responsible for turning a directory of camera/metadata files into
   a list of *scene dicts*, one per scene, each containing the sensor-agnostic keys
   the geometry code consumes:

   ``xml_fn``, ``catid``, ``sensor``, ``date``, ``scandir``, ``tdi``, ``geom``
   (a Shapely polygon footprint in EPSG:4326), the mean view-angle/GSD/sun
   attributes (``meansataz``, ``meansatel``, ``meanoffnadirviewangle``,
   ``meanintrackviewangle``, ``meancrosstrackviewangle``, ``meanproductgsd``,
   ``meansunaz``, ``meansunel``, ``cloudcover``), and — when ``geteph`` is True —
   ``eph_gdf`` (ephemeris GeoDataFrame in EPSG:4978), ``att_df`` (attitude
   DataFrame), and ``fp_gdf`` (footprint GeoDataFrame in EPSG:4326).



Attributes
----------

.. autoapisummary::

   asp_plot.sensors.SENSORS
   asp_plot.sensors.logger


Classes
-------

.. autoapisummary::

   asp_plot.sensors.SensorMetadata
   asp_plot.sensors.WorldViewMetadata


Functions
---------

.. autoapisummary::

   asp_plot.sensors.resolve_xml_inputs
   asp_plot.sensors.sensor_for_directory
   asp_plot.sensors.sensor_for_inputs


Module Contents
---------------

.. py:class:: SensorMetadata(directory)

   Bases: :py:obj:`abc.ABC`


   Abstract base class for a single sensor's metadata reader.

   A concrete reader discovers the scene files for one sensor in a directory and
   extracts a list of sensor-agnostic *scene dicts* (see the module docstring
   for the schema) that the stereo-pair geometry code can consume without
   knowing which sensor produced them.

   Subclasses must implement :meth:`detect` (so the sensor can be chosen
   automatically) and :meth:`get_scene_dicts`.

   .. attribute:: name

      Human-readable sensor name (e.g. ``"WorldView"``).

      :type: str

   .. attribute:: directory

      Path to the directory containing the sensor's metadata files.

      :type: str


   .. py:method:: detect(directory)
      :classmethod:

      :abstractmethod:


      Return True if this reader can handle the files in ``directory``.

      :param directory: Path to directory to inspect.
      :type directory: str

      :returns: Whether this sensor's metadata files are present.
      :rtype: bool



   .. py:method:: detect_files(image_list)
      :classmethod:

      :abstractmethod:


      Return True if this reader can handle the files in ``image_list``.

      The file-list counterpart of :meth:`detect`, used when the sensor must
      be chosen from an explicit list of inputs rather than a directory.

      :param image_list: Candidate metadata file paths.
      :type image_list: list of str

      :returns: Whether this sensor's metadata files are present in the list.
      :rtype: bool



   .. py:method:: get_scene_dicts()
      :abstractmethod:


      Return a list of per-scene metadata dictionaries.

      :returns: One sensor-agnostic scene dict per scene (see module docstring).
      :rtype: list of dict



   .. py:attribute:: directory


   .. py:attribute:: name
      :value: 'sensor'



.. py:class:: WorldViewMetadata(directory=None, image_list=None)

   Bases: :py:obj:`SensorMetadata`


   Metadata reader for WorldView satellite XML camera files.

   Parses WorldView (and other DigitalGlobe-heritage products that share the
   same XML format, e.g. GeoEye-1, QuickBird, IKONOS) satellite XML files to
   extract per-scene metadata, handling both single XML files and multiple XML
   tiles per scene (mosaicked with ``dg_mosaic``).

   This class is named for the *sensor family* (the stable WorldView name) and
   governs which reader parses the XML. It is intentionally distinct from the
   *attribution* check :func:`asp_plot.utils.detect_vantor_satellite`, which is
   named for the rights-holder (Vantor) and decides whether the "© Vantor"
   overlay applies. The two concerns use different names on purpose; see #137.

   .. attribute:: directory

      Path to directory containing XML files.

      :type: str

   .. attribute:: image_list

      List of XML files found in the directory.

      :type: list


   .. py:method:: detect(directory)
      :classmethod:


      Return True if non-ortho XML camera files are present.

      :param directory: Path to directory to inspect.
      :type directory: str

      :returns: Whether WorldView XML camera files were found.
      :rtype: bool



   .. py:method:: detect_files(image_list)
      :classmethod:


      Return True if any file in ``image_list`` is a camera XML.

      The file-list counterpart of :meth:`detect`, used to choose a reader
      for an explicit list of inputs (see :func:`sensor_for_inputs`). A file
      is a camera XML if it survives the non-camera basename filter (so
      ``README.XML`` / ``*ortho*.xml`` alone do not match).

      :param image_list: Candidate XML file paths.
      :type image_list: list of str

      :returns: Whether any camera XML files are present.
      :rtype: bool



   .. py:method:: getAtt(xml)

      Extract attitude data from XML file.

      Retrieves satellite attitude (orientation quaternion and covariance) data
      from the XML file.

      :param xml: Path to the XML file
      :type xml: str

      :returns: Array of shape (N, 15) containing attitude data with columns:
                point_num, q1, q2, q3, q4, and 10 covariance matrix elements
                (upper triangle of 4x4 matrix)
      :rtype: numpy.ndarray



   .. py:method:: getAtt_df(xml)

      Create a DataFrame from attitude data.

      Converts attitude data to a DataFrame with time index.

      :param xml: Path to the XML file
      :type xml: str

      :returns: DataFrame with attitude quaternions and covariance, time-indexed
      :rtype: pandas.DataFrame



   .. py:method:: getEphem(xml)

      Extract ephemeris data from XML file.

      Retrieves satellite ephemeris (position and velocity) data from the XML file.

      :param xml: Path to the XML file
      :type xml: str

      :returns: Array containing ephemeris data with columns:
                point_num, Xpos, Ypos, Zpos, Xvel, Yvel, Zvel, and covariance matrix elements
      :rtype: numpy.ndarray

      .. rubric:: Notes

      All coordinates are in Earth-Centered Fixed (ECF) reference frame.
      Units are meters for positions, meters/sec for velocities, and m^2 for covariance.



   .. py:method:: getEphem_gdf(xml)

      Create a GeoDataFrame from ephemeris data.

      Converts ephemeris data to a GeoDataFrame with time index and Point geometry.

      :param xml: Path to the XML file
      :type xml: str

      :returns: GeoDataFrame with ephemeris data and Point geometries in EPSG:4978
      :rtype: geopandas.GeoDataFrame

      .. rubric:: Notes

      The GeoDataFrame uses EPSG:4978 (Earth-Centered Earth-Fixed) CRS and
      has a time index corresponding to the acquisition times.



   .. py:method:: get_catid_xmls()

      Get a single representative XML file for each catalog ID.

      Groups the discovered XML files by their catalog ID (read from the XML
      content, not the filename) and resolves each scene to one XML: a scene
      delivered as a single XML is used as-is, while a scene tiled across
      multiple XMLs is mosaicked into one with ``dg_mosaic``.

      :returns: Dictionary mapping catalog IDs to a single XML file path.
      :rtype: dict

      :raises ValueError: If none of the discovered XML files contain a ``CATID`` tag.

      .. rubric:: Notes

      Mosaicking is decided per catalog ID, so a directory holding many
      distinct single-tile scenes is *not* mosaicked just because it contains
      more than two XML files. Mosaicking a tiled scene requires ``dg_mosaic``
      from the NASA Ames Stereo Pipeline on the system path.



   .. py:method:: get_id_dict(catid, xml, geteph=True)

      Get a dictionary of metadata for a specific catalog ID.

      Extracts metadata from XML file for a given catalog ID, including
      satellite parameters, acquisition angles, and geometry.

      :param catid: Catalog ID for the satellite image
      :type catid: str
      :param xml: Path to the XML file
      :type xml: str
      :param geteph: Whether to extract ephemeris data, default is True
      :type geteph: bool, optional

      :returns: Dictionary containing metadata for the catalog ID
      :rtype: dict

      .. rubric:: Notes

      The dictionary includes satellite ID, acquisition date, scan direction,
      TDI level, geometry information, and various mean angles and parameters.
      If geteph is True, also includes ephemeris and footprint GeoDataFrames.



   .. py:method:: get_scene_dicts()

      Get dictionaries of metadata for each catalog ID.

      Builds dictionaries of metadata for each catalog ID found in the XML files.

      :returns: List of dictionaries, one for each catalog ID, containing metadata
      :rtype: list



   .. py:method:: xml2poly(xml)

      Convert XML corner coordinates to Shapely Polygon.

      Reads XML file and converts corner coordinates to a Shapely Polygon geometry.

      :param xml: Path to the XML file
      :type xml: str

      :returns: Polygon geometry representing the image footprint
      :rtype: shapely.geometry.Polygon



   .. py:method:: xml2wkt(xml)

      Convert XML corner coordinates to WKT polygon string.

      Extracts corner coordinates from XML file and converts them to a
      Well-Known Text (WKT) polygon string.

      :param xml: Path to the XML file
      :type xml: str

      :returns: WKT polygon string representation of image footprint
      :rtype: str

      .. rubric:: Notes

      Uses ULLON/ULLAT, URLON/URLAT, LRLON/LRLAT, LLLON/LLLAT tags
      (Upper-Left, Upper-Right, Lower-Right, Lower-Left corners).



   .. py:attribute:: name
      :value: 'WorldView'



.. py:function:: resolve_xml_inputs(inputs, recursive=True)

   Expand files, directories, and glob patterns into XML file paths.

   Lets a user point the tools at messy inputs without a fixed directory
   structure — e.g. ``geom_plot *.XML`` (already expanded by the shell),
   ``geom_plot scene1.xml scene2.xml``, ``geom_plot delivery_dir/``, or a mix.

   Each item of ``inputs`` may be:

   - a path to an XML file (included directly),
   - a directory (searched with :meth:`WorldViewMetadata._discover_xmls`, which
     is shallow-first and falls back to a recursive search), or
   - a glob pattern (expanded with :func:`glob.glob`).

   Results are de-duplicated (by absolute path) and returned sorted. Note that
   sensor-specific filtering of non-camera XMLs (``README.XML``, ortho
   products) is applied by the reader, not here.

   :param inputs: One or more files, directories, and/or glob patterns.
   :type inputs: str or os.PathLike or iterable of those
   :param recursive: Passed through to directory discovery and ``**`` glob expansion.
                     Default True.
   :type recursive: bool, optional

   :returns: Sorted, de-duplicated XML file paths.
   :rtype: list of str


.. py:function:: sensor_for_directory(directory)

   Detect and instantiate the appropriate sensor reader for a directory.

   Iterates the ``SENSORS`` registry and returns an instance of the first
   reader whose :meth:`SensorMetadata.detect` matches the directory contents.

   :param directory: Path to directory containing camera/metadata files.
   :type directory: str

   :returns: An initialized reader for the detected sensor.
   :rtype: SensorMetadata

   :raises ValueError: If no registered sensor reader matches the directory contents.


.. py:function:: sensor_for_inputs(inputs)

   Detect and instantiate the appropriate sensor reader for explicit inputs.

   The file-list counterpart of :func:`sensor_for_directory`. Resolves
   ``inputs`` (files, directories, and/or globs) into a list of XML files,
   then returns an instance of the first registered reader whose
   :meth:`SensorMetadata.detect_files` matches.

   :param inputs: One or more files, directories, and/or glob patterns.
   :type inputs: str or os.PathLike or iterable of those

   :returns: An initialized reader for the detected sensor.
   :rtype: SensorMetadata

   :raises ValueError: If no XML files are found, or no registered sensor reader matches them.


.. py:data:: SENSORS

.. py:data:: logger

