asp_plot.csm_camera
===================

.. py:module:: asp_plot.csm_camera


Attributes
----------

.. autoapisummary::

   asp_plot.csm_camera.ASP_TO_CSM_SHIFT


Functions
---------

.. autoapisummary::

   asp_plot.csm_camera.csm_camera_summary_plot
   asp_plot.csm_camera.estim_satellite_orientation
   asp_plot.csm_camera.format_stat_value
   asp_plot.csm_camera.getLineAtTime
   asp_plot.csm_camera.getTimeAtLine
   asp_plot.csm_camera.get_orbit_plot_gdf
   asp_plot.csm_camera.isLinescan
   asp_plot.csm_camera.plot_stats_text
   asp_plot.csm_camera.poly_fit
   asp_plot.csm_camera.read_angles
   asp_plot.csm_camera.read_csm_cam
   asp_plot.csm_camera.read_frame_cam_dict
   asp_plot.csm_camera.read_frame_csm_cam
   asp_plot.csm_camera.read_linescan_pos_rot
   asp_plot.csm_camera.read_positions_rotations
   asp_plot.csm_camera.read_positions_rotations_from_file
   asp_plot.csm_camera.read_tsai_cam
   asp_plot.csm_camera.reproject_ecef
   asp_plot.csm_camera.roll_pitch_yaw
   asp_plot.csm_camera.toCsmPixel
   asp_plot.csm_camera.tsai_list_to_gdf


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

.. py:function:: csm_camera_summary_plot(cam1_list, cam2_list=None, map_crs=None, title=None, trim=True, shared_scales=False, log_scale_positions=False, log_scale_angles=False, upper_magnitude_percentile=95, figsize=(20, 15), save_dir=None, fig_fn=None, add_basemap=False, **ctx_kwargs)

   Generate a comprehensive summary plot comparing camera parameters.

   :param cam1_list: List containing paths to the original and optimized camera files for the first camera
   :type cam1_list: list
   :param cam2_list: List containing paths to the original and optimized camera files for the second camera,
                     default is None
   :type cam2_list: list or None, optional
   :param map_crs: EPSG code for the coordinate reference system for map plots, default is None (use ECEF)
   :type map_crs: int or None, optional
   :param title: Additional title text to append to the default title, default is None
   :type title: str or None, optional
   :param trim: Whether to trim data to image lines for linescan cameras, default is True
   :type trim: bool, optional
   :param shared_scales: Whether to use the same y-axis scale for all position/angle plots, default is False
   :type shared_scales: bool, optional
   :param log_scale_positions: Whether to use logarithmic scale for position difference plots, default is False
   :type log_scale_positions: bool, optional
   :param log_scale_angles: Whether to use logarithmic scale for angle difference plots, default is False
   :type log_scale_angles: bool, optional
   :param upper_magnitude_percentile: Percentile for setting colorbar upper limit, default is 95
   :type upper_magnitude_percentile: int, optional
   :param figsize: Figure size (width, height) in inches, default is (20, 15)
   :type figsize: tuple, optional
   :param save_dir: Directory to save the plot, default is None (don't save)
   :type save_dir: str or None, optional
   :param fig_fn: Filename for the saved plot, default is None
   :type fig_fn: str or None, optional
   :param add_basemap: Whether to add a basemap to map plots, default is False
   :type add_basemap: bool, optional
   :param \*\*ctx_kwargs: Additional keyword arguments passed to contextily.add_basemap()

   .. rubric:: Notes

   This function creates a comprehensive visualization comparing original and
   optimized camera parameters. It shows the spatial distribution of position
   and orientation differences, as well as detailed plots of individual
   components (x, y, z, roll, pitch, yaw). If two cameras are provided, the
   comparison is shown for both cameras in a larger figure.


.. py:function:: estim_satellite_orientation(positions)

   Estimate satellite orientation at each position.

   :param positions: List of satellite positions in ECEF coordinates
   :type positions: list of array-like

   :returns: List of rotation matrices representing satellite orientation
   :rtype: list of numpy.ndarray

   .. rubric:: Notes

   For each position, computes a local coordinate system where:
   - x axis is the direction of motion
   - z points roughly down (towards Earth center)
   - y is perpendicular to both x and z


.. py:function:: format_stat_value(value)

   Format a numeric value with appropriate precision.

   :param value: The numeric value to format
   :type value: float

   :returns: Formatted string representation of the value
   :rtype: str

   .. rubric:: Notes

   Uses scientific notation for very small values (abs(value) < 0.01)
   and fixed-point notation with 2 decimal places otherwise.


.. py:function:: getLineAtTime(time, model)

   Get the line number at a given time in a linescan camera model.

   :param time: Time to convert to line number
   :type time: float
   :param model: CSM camera model parameters
   :type model: dict

   :returns: Line number corresponding to the given time
   :rtype: float

   :raises Exception: If the model does not have a linear relationship between time and lines

   .. rubric:: Notes

   This function computes the line number in the image corresponding
   to the given time, assuming a linear relationship between time and
   line number. Code adapted from get_line_at_time() in CsmUtils.cc.


.. py:function:: getTimeAtLine(model, line)

   Find the time at a given line in a linescan camera model.

   :param model: CSM camera model parameters
   :type model: dict
   :param line: Line number in the image (0-based)
   :type line: int

   :returns: Time corresponding to the given line
   :rtype: float

   .. rubric:: Notes

   The time is computed using the linear mapping between line number
   and time defined in the CSM model. Code adapted from get_time_at_line()
   in CsmUtils.cc and getImageTime() in UsgsAstroLsSensorModel.cpp.


.. py:function:: get_orbit_plot_gdf(original_camera, optimized_camera, map_crs=None, trim=True)

   Create a GeoDataFrame containing camera positions and orientation differences.

   :param original_camera: Path to the original camera file
   :type original_camera: str
   :param optimized_camera: Path to the optimized camera file
   :type optimized_camera: str
   :param map_crs: EPSG code for the target coordinate system, default is None (keep ECEF)
   :type map_crs: int or None, optional
   :param trim: Whether to trim data to only the first and last image lines for linescan
                cameras, default is True
   :type trim: bool, optional

   :returns: A GeoDataFrame containing camera positions and orientation data with
             columns for position differences, angle differences, and original values
   :rtype: geopandas.GeoDataFrame

   .. rubric:: Notes

   This function compares the original and optimized camera models and
   calculates the differences in position and orientation. For linescan
   cameras, it optionally trims the data to only include samples corresponding
   to the actual image lines.


.. py:function:: isLinescan(cam_file)

   Check if a camera file is for a linescan sensor.

   :param cam_file: Path to the camera file
   :type cam_file: str

   :returns: True if the camera is a linescan sensor, False otherwise
   :rtype: bool

   .. rubric:: Notes

   This function reads the first line of the camera file to check if
   it contains the string "LINE_SCAN", which indicates a linescan camera.


.. py:function:: plot_stats_text(ax, mean, std, unit='m')

   Add a text annotation showing statistics to a plot.

   :param ax: The axis on which to add the text
   :type ax: matplotlib.axes.Axes
   :param mean: Mean value to display
   :type mean: float
   :param std: Standard deviation value to display
   :type std: float
   :param unit: Unit to use for display, default is 'm'
   :type unit: str, optional

   .. rubric:: Notes

   The text is displayed in the lower left corner of the plot with
   a white background box for readability.


.. py:function:: poly_fit(X, Y)

   Fit a linear polynomial to data and return the fitted values.

   :param X: Independent variable values
   :type X: array-like
   :param Y: Dependent variable values
   :type Y: array-like

   :returns: Fitted Y values from a degree 1 polynomial fit
   :rtype: numpy.ndarray


.. py:function:: read_angles(orig_cams, opt_cams, ref_cams)

   Extract and convert camera orientations to roll, pitch, yaw angles.

   :param orig_cams: List of paths to original camera files
   :type orig_cams: list of str
   :param opt_cams: List of paths to optimized camera files
   :type opt_cams: list of str
   :param ref_cams: List of paths to reference camera files (can be empty)
   :type ref_cams: list of str

   :returns: Tuple containing (orig_rotation_angles, opt_rotation_angles) where:
             - orig_rotation_angles is a list of Euler angles for original cameras
             - opt_rotation_angles is a list of Euler angles for optimized cameras
   :rtype: tuple

   :raises SystemExit: If the number of original and reference cameras don't match

   .. rubric:: Notes

   This function extracts the orientation of cameras and converts them
   to Euler angles (roll, pitch, yaw) relative to a reference orientation.
   If reference cameras are not provided, it estimates the reference
   orientation from the camera positions.


.. py:function:: read_csm_cam(json_file)

   Read a CSM model state file in JSON format.

   :param json_file: Path to the CSM JSON state file
   :type json_file: str

   :returns: Dictionary containing the CSM model parameters
   :rtype: dict

   .. rubric:: Notes

   CSM JSON files sometimes have text before the actual JSON content.
   This function handles that by finding the first open brace and
   parsing the JSON from that point.


.. py:function:: read_frame_cam_dict(cam)

   Read a frame camera model file into a dictionary.

   :param cam: Path to the camera file (.tsai or .json)
   :type cam: str

   :returns: Dictionary containing camera parameters
   :rtype: dict

   :raises Exception: If the file extension is not recognized


.. py:function:: read_frame_csm_cam(json_file)

   Read position and orientation from a CSM Frame camera file.

   :param json_file: Path to the CSM JSON state file
   :type json_file: str

   :returns: Dictionary containing camera center and rotation matrix
   :rtype: dict

   .. rubric:: Notes

   This function extracts the camera position and orientation from a
   CSM frame camera model. The camera position is given by the first three
   parameters, and the orientation is represented as a quaternion that
   is converted to a rotation matrix.


.. py:function:: read_linescan_pos_rot(json_file)

   Read positions and rotations from a CSM linescan camera file.

   :param json_file: Path to the CSM JSON state file
   :type json_file: str

   :returns: Tuple containing (positions, rotations) where:
             - positions is a list of camera positions
             - rotations is a list of rotation matrices
   :rtype: tuple

   .. rubric:: Notes

   Linescan cameras have different positions and orientations for each
   line in the image. This function extracts the full list of positions
   and orientations from the CSM model.


.. py:function:: read_positions_rotations(cams)

   Read positions and rotations from multiple camera files.

   :param cams: List of paths to camera files
   :type cams: list of str

   :returns: Tuple containing (positions, rotations) where:
             - positions is a list of camera positions
             - rotations is a list of rotation matrices
   :rtype: tuple

   :raises SystemExit: If the number of positions and rotations don't match

   .. rubric:: Notes

   This function reads all the camera files and concatenates their
   positions and rotations into single lists.


.. py:function:: read_positions_rotations_from_file(cam_file)

   Read positions and rotations from a camera file.

   :param cam_file: Path to the camera file
   :type cam_file: str

   :returns: Tuple containing (positions, rotations) where:
             - positions is a list of camera positions
             - rotations is a list of rotation matrices
   :rtype: tuple

   .. rubric:: Notes

   This function handles both linescan and frame cameras. For linescan
   cameras, it returns multiple positions and rotations corresponding
   to different lines in the image. For frame cameras, it returns a
   single position and rotation.


.. py:function:: read_tsai_cam(tsai)

   Read a TSAI frame camera model into a dictionary.

   :param tsai: Path to ASP frame camera model file (.tsai)
   :type tsai: str

   :returns: Dictionary containing camera model parameters
   :rtype: dict

   .. rubric:: Notes

   TSAI is a camera model format used by ASP. It contains parameters such as
   focal length, optical center, camera position, and orientation. See ASP
   documentation for more details:
   https://stereopipeline.readthedocs.io/en/latest/pinholemodels.html


.. py:function:: reproject_ecef(positions, to_epsg=4326)

   Reproject ECEF coordinates to a specified EPSG coordinate system.

   :param positions: A 2D array of ECEF coordinates, where each row represents a point
   :type positions: numpy.ndarray
   :param to_epsg: The EPSG code of the target coordinate system, default is 4326 (WGS84)
   :type to_epsg: int, optional

   :returns: A 2D array of reprojected coordinates in the target EPSG coordinate system
   :rtype: numpy.ndarray

   .. rubric:: Notes

   ECEF (Earth-Centered, Earth-Fixed) coordinates are a 3D Cartesian coordinate
   system with the origin at the center of the Earth. This function converts
   those coordinates to a different coordinate system specified by an EPSG code.


.. py:function:: roll_pitch_yaw(rot_mat, ref_rot_mat)

   Calculate roll, pitch, and yaw angles relative to a reference orientation.

   :param rot_mat: Rotation matrix for the camera
   :type rot_mat: numpy.ndarray
   :param ref_rot_mat: Reference rotation matrix
   :type ref_rot_mat: numpy.ndarray

   :returns: Array of Euler angles [roll, pitch, yaw] in degrees
   :rtype: numpy.ndarray

   .. rubric:: Notes

   This function calculates the orientation of a camera relative to a
   reference orientation, and returns the result as Euler angles in
   roll, pitch, yaw (rotation around x, y, z axes) in degrees.


.. py:function:: toCsmPixel(asp_pix)

   Convert an ASP pixel coordinate to a CSM pixel coordinate.

   :param asp_pix: ASP pixel coordinates as [x, y]
   :type asp_pix: array-like

   :returns: CSM pixel coordinates
   :rtype: numpy.ndarray

   .. rubric:: Notes

   ASP and CSM use slightly different pixel coordinate conventions.
   CSM pixel coordinates are shifted by 0.5 pixels relative to ASP.
   Code copied from CsmModel.cc in ASP.


.. py:function:: tsai_list_to_gdf(tsai_fn_list)

   Convert a list of TSAI camera files to a GeoDataFrame.

   :param tsai_fn_list: List of paths to TSAI camera files
   :type tsai_fn_list: list of str

   :returns: GeoDataFrame containing camera model parameters with
             a Point geometry column for camera centers
   :rtype: geopandas.GeoDataFrame


.. py:data:: ASP_TO_CSM_SHIFT
   :value: 0.5


