Skip to content

Matplotlib Plots

mpl_plots

plot_trajectories

plot_trajectories(trajectories: list[Trajectory], scatter_3d: bool = False, plot_settings: MPLPlotSettings = MPLPlotSettings()) -> tuple[Figure, Figure, Figure | None]

Plots the trajectories in 2d or 3d using matplotlib.

This function creates one 2D or 3D plot for the xy(z) coordinates of the trajectories, one subplot for the xyz coordinates and one subplot for the rpy angles.

Parameters:

  • trajectories (List[Trajectory]) –

    List of trajectories to plot.

  • scatter_3d (bool, default: False ) –

    Whether to create a 3D scatter plot. Defaults to False.

Returns:

  • tuple[Figure, Figure, Figure | None]

    Tuple[Figure, Figure, Union[Figure, None]]: Figures for the position, xyz and rpy plots.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_trajectories(
    trajectories: list[Trajectory],
    scatter_3d: bool = False,
    plot_settings: MPLPlotSettings = MPLPlotSettings(),
) -> tuple[Figure, Figure, Figure | None]:
    """Plots the trajectories in 2d or 3d using matplotlib.

    This function creates one 2D or 3D plot for the xy(z) coordinates of the trajectories,
    one subplot for the xyz coordinates and one subplot for the rpy angles.

    Args:
        trajectories (List[Trajectory]): List of trajectories to plot.
        scatter_3d (bool, optional): Whether to create a 3D scatter plot. Defaults to False.

    Returns:
        Tuple[Figure, Figure, Union[Figure, None]]: Figures for the position, xyz and rpy plots.
    """
    fig_pos = plot_positions(trajectories=trajectories, scatter_3d=scatter_3d)
    fig_xyz = plot_xyz(trajectories=trajectories, plot_settings=plot_settings)
    fig_rpy = plot_rpy(trajectories=trajectories, plot_settings=plot_settings)
    return fig_pos, fig_xyz, fig_rpy

plot_correlation_heatmap

plot_correlation_heatmap(estimated_parameters: AlignmentParameters, enabled_only: bool = True) -> Figure

Plots the correlation heatmap of the alignment parameters using matplotlib.

Parameters:

  • estimated_parameters (AlignmentParameters) –

    Estimated parameters.

  • enabled_only (bool, default: True ) –

    Whether to consider only enabled parameters. Defaults to True.

Returns:

  • Figure

    plt.Figure: Correlation heatmap figure.

Source code in trajectopy\visualization\mpl_plots.py
def plot_correlation_heatmap(estimated_parameters: AlignmentParameters, enabled_only: bool = True) -> Figure:
    """Plots the correlation heatmap of the alignment parameters using matplotlib.

    Args:
        estimated_parameters (AlignmentParameters): Estimated parameters.
        enabled_only (bool, optional): Whether to consider only enabled parameters. Defaults to True.

    Returns:
        plt.Figure: Correlation heatmap figure.
    """
    covariance_matrix = estimated_parameters.get_covariance_matrix(enabled_only=enabled_only)
    std_devs = np.sqrt(np.diag(covariance_matrix))
    correlation_matrix = covariance_matrix / np.outer(std_devs, std_devs)
    np.fill_diagonal(correlation_matrix, np.nan)
    fig, ax = plt.subplots()
    ax.grid(False)
    im, _ = _heatmap(
        correlation_matrix,
        estimated_parameters.params_labels(enabled_only=enabled_only, lower_case=True),
        estimated_parameters.params_labels(enabled_only=enabled_only, lower_case=True),
        ax=ax,
        cmap="coolwarm",
        cbarlabel="Correlation",
        cbar_kw={"format": "%.2f"},
    )
    _annotate_heatmap(im, valfmt="{x:.2f}")
    ax.set_aspect("auto")

    plt.tight_layout()
    return fig

plot_covariance_heatmap

plot_covariance_heatmap(estimated_parameters: AlignmentParameters, enabled_only: bool = True) -> Figure

Plots the covariance heatmap of the alignment parameters using matplotlib.

Parameters:

  • estimated_parameters (AlignmentParameters) –

    Estimated parameters.

  • enabled_only (bool, default: True ) –

    Whether to consider only enabled parameters. Defaults to True.

Returns:

  • Figure

    plt.Figure: Covariance heatmap figure.

Source code in trajectopy\visualization\mpl_plots.py
def plot_covariance_heatmap(estimated_parameters: AlignmentParameters, enabled_only: bool = True) -> Figure:
    """Plots the covariance heatmap of the alignment parameters using matplotlib.

    Args:
        estimated_parameters (AlignmentParameters): Estimated parameters.
        enabled_only (bool, optional): Whether to consider only enabled parameters. Defaults to True.

    Returns:
        plt.Figure: Covariance heatmap figure.
    """
    covariance_matrix = estimated_parameters.get_covariance_matrix(enabled_only=enabled_only)
    fig, ax = plt.subplots()
    ax.grid(False)
    im, _ = _heatmap(
        covariance_matrix,
        estimated_parameters.params_labels(enabled_only=enabled_only, lower_case=True),
        estimated_parameters.params_labels(enabled_only=enabled_only, lower_case=True),
        ax=ax,
        cmap="coolwarm",
        cbarlabel="Covariance",
        cbar_kw={"format": "%.2f"},
    )
    _annotate_heatmap(im, valfmt="{x:.3f}")
    ax.set_aspect("auto")
    plt.tight_layout()
    return fig

plot_ate_3d

plot_ate_3d(ate_results: list[ATEResult], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure

Plots the ATE results in 2D using matplotlib.

Parameters:

Returns:

  • Figure ( Figure ) –

    Figure containing the plot.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_ate_3d(ate_results: list[ATEResult], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure:
    """
    Plots the ATE results in 2D using matplotlib.

    Args:
        ate_results (List[ATEResult]): List of ATE results.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().

    Returns:
        Figure: Figure containing the plot.
    """
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")

    for ate_result in ate_results:
        if len(ate_result.index) == 0:
            logger.warning("Skipping %s as it has no data", ate_result.name)
            continue
        ax.plot(
            (
                ate_result.pos_dev_cross_h * plot_settings.unit_multiplier
                if plot_settings.directed_ate
                else ate_result.pos_dev_x * plot_settings.unit_multiplier
            ),
            (
                ate_result.pos_dev_along * plot_settings.unit_multiplier
                if plot_settings.directed_ate
                else ate_result.pos_dev_y * plot_settings.unit_multiplier
            ),
            (
                ate_result.pos_dev_cross_v * plot_settings.unit_multiplier
                if plot_settings.directed_ate
                else ate_result.pos_dev_z * plot_settings.unit_multiplier
            ),
            ".",
            label=ate_result.name,
        )

    ax.set_xlabel(f"{'Horizontal Cross-Track' if plot_settings.directed_ate else 'X'} {plot_settings.unit_str}")
    ax.set_ylabel(f"{'Along-Track' if plot_settings.directed_ate else 'Y'} {plot_settings.unit_str}")
    ax.set_zlabel(f"{'Vertical Cross-Track' if plot_settings.directed_ate else 'Z'} {plot_settings.unit_str}")

    ax.legend()
    plt.tight_layout()
    return fig

plot_ate_bars

plot_ate_bars(ate_results: list[ATEResult], plot_settings: MPLPlotSettings = MPLPlotSettings(), mode: str = 'positions') -> Figure

Plots multiple ATE results as bars for different characteristics.

Shows min, max, mean, median, rms, std using matplotlib.

Parameters:

  • ate_results (List[ATEResult]) –

    List of ATE results.

  • plot_settings (MPLPlotSettings, default: MPLPlotSettings() ) –

    Plot settings. Defaults to MPLPlotSettings().

  • mode (str, default: 'positions' ) –

    Mode to plot. Either 'positions' or 'rotations'. Defaults to 'positions'.

Returns:

  • Figure ( Figure ) –

    Bar plot figure.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_ate_bars(
    ate_results: list[ATEResult],
    plot_settings: MPLPlotSettings = MPLPlotSettings(),
    mode: str = "positions",
) -> Figure:
    """Plots multiple ATE results as bars for different characteristics.

    Shows min, max, mean, median, rms, std using matplotlib.

    Args:
        ate_results (List[ATEResult]): List of ATE results.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().
        mode (str, optional): Mode to plot. Either 'positions' or 'rotations'. Defaults to 'positions'.

    Returns:
        Figure: Bar plot figure.
    """
    fig, ax = plt.subplots()

    if not ate_results:
        return fig

    bar_width = 0.9 / len(ate_results)
    characteristics = ["Min", "Max", "Mean", "Median", "RMS", "STD"]
    unit = plot_settings.unit_str if mode == "positions" else "[°]"
    spacings = np.linspace(
        -bar_width * (len(ate_results) - 1) / 2,
        bar_width * (len(ate_results) - 1) / 2,
        len(ate_results),
    )
    x_positions = np.arange(len(characteristics))
    for deviation, spacing in zip(ate_results, spacings):
        if mode == "rotations" and deviation.abs_dev.rot_dev is None:
            continue

        if mode == "positions":
            data = [
                deviation.pos_dev_min * plot_settings.unit_multiplier,
                deviation.pos_dev_max * plot_settings.unit_multiplier,
                deviation.pos_ate * plot_settings.unit_multiplier,
                deviation.pos_dev_median * plot_settings.unit_multiplier,
                deviation.pos_dev_rms * plot_settings.unit_multiplier,
                deviation.pos_dev_std * plot_settings.unit_multiplier,
            ]
        elif mode == "rotations":
            data = [
                np.rad2deg(deviation.rot_dev_min),
                np.rad2deg(deviation.rot_dev_max),
                np.rad2deg(deviation.rot_ate),
                np.rad2deg(deviation.rot_dev_median),
                np.rad2deg(deviation.rot_dev_rms),
                np.rad2deg(deviation.rot_dev_std),
            ]
        else:
            raise ValueError("Mode must be either 'positions' or 'rotations'")
        ax.bar(x_positions + spacing, data, width=bar_width, label=deviation.name)

    ax.set_xlabel("Characteristic")
    ax.set_ylabel(f"Value {unit}")
    ax.set_xticks(x_positions)
    ax.set_xticklabels(characteristics)
    ax.legend()

    return fig

plot_compact_ate_hist

plot_compact_ate_hist(ate_result: ATEResult, plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure

Plots compact ATE histograms for the given ATEResult. The plot contains histograms for the position deviations and, if available, the rotation deviations.

Parameters:

Returns:

  • Figure ( Figure ) –

    Figure containing the plot.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_compact_ate_hist(ate_result: ATEResult, plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure:
    """
    Plots compact ATE histograms for the given ATEResult.
    The plot contains histograms for the position deviations and, if available, the rotation deviations.

    Args:
        ate_result (ATEResult): ATE result to plot.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().

    Returns:
        Figure: Figure containing the plot.
    """
    fig = plt.figure()
    pos_ax = plt.subplot(2, 1, 1)
    plot_position_ate_hist(ate_result, plot_settings)
    pos_ax.ticklabel_format(style="sci", axis="y", scilimits=(0, 0))

    if ate_result.abs_dev.rot_dev is not None:
        rot_ax = plt.subplot(2, 1, 2)
        plot_rotation_ate_hist(ate_result, plot_settings)
        rot_ax.ticklabel_format(style="sci", axis="y", scilimits=(0, 0))

    plt.tight_layout()
    return fig

plot_ate

plot_ate(ate_results: ATEResult | list[ATEResult], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure

Plots ATE for the given ATEResult(s) as a line plot using matplotlib. If available, the plot contains the position and rotation deviations. The x-axis depends on the sorting of the trajectory.

Parameters:

Returns:

  • Figure ( Figure ) –

    Figure containing the plot.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_ate(
    ate_results: ATEResult | list[ATEResult],
    plot_settings: MPLPlotSettings = MPLPlotSettings(),
) -> Figure:
    """
    Plots ATE for the given ATEResult(s) as a line plot using matplotlib.
    If available, the plot contains the position and rotation deviations.
    The x-axis depends on the sorting of the trajectory.

    Args:
        ate_results (Union[ATEResult, List[ATEResult]]): ATE result(s) to plot.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().

    Returns:
        Figure: Figure containing the plot.
    """
    deviation_list = ate_results if isinstance(ate_results, list) else [ate_results]
    trajectories_list = [dev.trajectory for dev in deviation_list]

    trajectories_sorting = get_sorting(traj.sorting for traj in trajectories_list)
    all_unix = all(traj.is_unix_time for traj in trajectories_list)
    use_datetime_axis = _use_datetime_axis(trajectories_sorting, all_unix, plot_settings)
    x_label = (
        "relative time [s]"
        if plot_settings.use_relative_timestamps and trajectories_sorting == TrajectoriesSorting.ALL_TIME
        else derive_xlabel_from_sortings(trajectories_sorting, all_unix)
    )
    time_reference = min((traj.timestamps[0] for traj in trajectories_list if len(traj.timestamps) > 0), default=0.0)

    fig = plt.figure()

    ax_pos = plt.subplot(2, 1, 1)
    ax_pos.set_xlabel(x_label)
    ax_pos.set_ylabel(f"Deviation {plot_settings.unit_str}")
    if use_datetime_axis:
        ax_pos.xaxis.set_major_formatter(DATE_FORMATTER)

    if any(dev.abs_dev.rot_dev for dev in deviation_list):
        ax_rot = plt.subplot(2, 1, 2)
        ax_rot.set_xlabel(x_label)
        ax_rot.set_ylabel("Deviation [°]")
        if use_datetime_axis:
            ax_rot.xaxis.set_major_formatter(DATE_FORMATTER)
    else:
        ax_rot = None

    for dev in deviation_list:
        if len(dev.index) == 0:
            logger.warning("Skipping %s as it has no data", dev.name)
            continue

        if use_datetime_axis:
            index = dev.trajectory.datetimes
        elif plot_settings.use_relative_timestamps and dev.trajectory.sorting == Sorting.TIME:
            index = dev.trajectory.timestamps - time_reference
        else:
            index = dev.index

        ax_pos.plot(index, dev.pos_dev_comb * plot_settings.unit_multiplier)
        ax_pos.set_xlim(index[0], index[-1])
        if ax_rot is not None:
            ax_rot.plot(index, np.rad2deg(dev.rot_dev_comb))
            ax_rot.set_xlim(index[0], index[-1])

    fig.legend([dev.name for dev in deviation_list], ncol=3, loc="upper center")
    plt.tight_layout()
    return fig

plot_ate_dof

plot_ate_dof(ate_result: ATEResult, plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure

Plots ATE DOF (Degrees of Freedom) for the given ATEResult as a line plot using matplotlib.

The DOF plot shows the deviations in the x, y, and z directions for position and rotation.

Parameters:

Returns:

  • Figure ( Figure ) –

    Figure containing the plot.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_ate_dof(
    ate_result: ATEResult,
    plot_settings: MPLPlotSettings = MPLPlotSettings(),
) -> Figure:
    """Plots ATE DOF (Degrees of Freedom) for the given ATEResult as a line plot using matplotlib.

    The DOF plot shows the deviations in the x, y, and z directions for position and rotation.

    Args:
        ate_result (ATEResult): ATE result to plot.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().

    Returns:
        Figure: Figure containing the plot.
    """
    trajectory = ate_result.trajectory
    x_label = derive_xlabel_from_sortings(
        TrajectoriesSorting.ALL_SPATIAL if trajectory.sorting == Sorting.PATH_LENGTH else TrajectoriesSorting.ALL_TIME,
        trajectory.is_unix_time,
    )
    if plot_settings.use_relative_timestamps and trajectory.sorting == Sorting.TIME:
        x_label = "relative time [s]"

    use_datetime_axis = (
        trajectory.is_unix_time and trajectory.sorting == Sorting.TIME and not plot_settings.use_relative_timestamps
    )

    fig = plt.figure()

    ax_pos = plt.subplot(2, 1, 1)
    ax_pos.set_xlabel(x_label)
    ax_pos.set_ylabel(f"Deviation {plot_settings.unit_str}")
    if use_datetime_axis:
        ax_pos.xaxis.set_major_formatter(DATE_FORMATTER)

    if ate_result.has_orientation:
        ax_rot = plt.subplot(2, 1, 2)
        ax_rot.set_xlabel(x_label)
        ax_rot.set_ylabel("Deviation [°]")
        if use_datetime_axis:
            ax_rot.xaxis.set_major_formatter(DATE_FORMATTER)
    else:
        ax_rot = None

    if len(ate_result.index) == 0:
        logger.warning("Skipping %s as it has no data", ate_result.name)
        return fig

    pos_dev_x = ate_result.pos_dev_along if plot_settings.directed_ate else ate_result.pos_dev_x
    pos_dev_y = ate_result.pos_dev_cross_h if plot_settings.directed_ate else ate_result.pos_dev_y
    pos_dev_z = ate_result.pos_dev_cross_v if plot_settings.directed_ate else ate_result.pos_dev_z

    if use_datetime_axis:
        index = ate_result.trajectory.datetimes
    elif plot_settings.use_relative_timestamps and trajectory.sorting == Sorting.TIME:
        index = ate_result.trajectory.timestamps - ate_result.trajectory.timestamps[0]
    else:
        index = ate_result.index

    ax_pos.plot(
        index,
        pos_dev_x * plot_settings.unit_multiplier,
        label="Along-Track" if plot_settings.directed_ate else "X",
    )
    ax_pos.plot(
        index,
        pos_dev_y * plot_settings.unit_multiplier,
        label="Horizontal Cross-Track" if plot_settings.directed_ate else "Y",
    )
    ax_pos.plot(
        index,
        pos_dev_z * plot_settings.unit_multiplier,
        label="Vertical Cross-Track" if plot_settings.directed_ate else "Z",
    )
    ax_pos.legend()
    ax_pos.set_xlim(index[0], index[-1])

    if ax_rot is not None:
        ax_rot.plot(index, np.rad2deg(ate_result.rot_dev_x), label="Roll")
        ax_rot.plot(index, np.rad2deg(ate_result.rot_dev_y), label="Pitch")
        ax_rot.plot(index, np.rad2deg(ate_result.rot_dev_z), label="Yaw")
        ax_rot.set_xlim(index[0], index[-1])
        ax_rot.legend()

    ax_pos.set_title(f"{ate_result.name}")
    plt.tight_layout()
    return fig

plot_ate_edf

plot_ate_edf(ate_results: ATEResult | list[ATEResult], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure

Plots ATE EDF for the given ATEResult(s) as a line plot using matplotlib. The EDF (Empirical Distribution Function) shows the cumulative probability of the deviations. Using this plot, one can easily see how many percent of the deviations are below a certain value.

Parameters:

Returns:

  • Figure ( Figure ) –

    Figure containing the plot.

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_ate_edf(
    ate_results: ATEResult | list[ATEResult],
    plot_settings: MPLPlotSettings = MPLPlotSettings(),
) -> Figure:
    """
    Plots ATE EDF for the given ATEResult(s) as a line plot using matplotlib.
    The EDF (Empirical Distribution Function) shows the cumulative probability of the deviations.
    Using this plot, one can easily see how many percent of the deviations are below a certain value.

    Args:
        ate_results (Union[ATEResult, List[ATEResult]]): ATE result to plot.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().

    Returns:
        Figure: Figure containing the plot.
    """
    deviation_list = ate_results if isinstance(ate_results, list) else [ate_results]

    fig = plt.figure()

    plot_position_ate_edf(deviation_list, plot_settings)
    plot_rotation_ate_edf(deviation_list)

    fig.legend([dev.name for dev in deviation_list], ncol=3, loc="upper center")
    plt.tight_layout()
    return fig

plot_rpe

plot_rpe(rpe_results: list[RPEResult]) -> tuple[Figure, Figure]

Plots the RPE results as a line plot with violin plots for the position and rotation deviations.

Depending on the pair distance unit, the unit of the position deviations is either in meters/meters (%) or meters/seconds. The unit of the rotation deviations is respectively in degrees/m or degrees/second.

Parameters:

  • rpe_results (list[RelativeTrajectoryDeviations]) –

    list of RelativeTrajectoryDeviations

Returns:

  • tuple[Figure, Figure]

    Tuple[Figure, Figure]: metric and time RPE plots

Source code in trajectopy\visualization\mpl_plots.py
def plot_rpe(rpe_results: list[RPEResult]) -> tuple[Figure, Figure]:
    """Plots the RPE results as a line plot with violin plots for the position and rotation deviations.

    Depending on the pair distance unit, the unit of the position deviations
    is either in meters/meters (%) or meters/seconds. The unit of the rotation
    deviations is respectively in degrees/m or degrees/second.

    Args:
        rpe_results (list[RelativeTrajectoryDeviations]): list of RelativeTrajectoryDeviations

    Returns:
        Tuple[Figure, Figure]: metric and time RPE plots

    """
    if not isinstance(rpe_results, list):
        rpe_results = [rpe_results]

    fig_metric, (fig_pos_metric, fig_rot_metric) = plt.subplots(2, 1)
    fig_time, (fig_pos_time, fig_rot_time) = plt.subplots(2, 1)

    fig_pos_metric.set_ylabel("Position RPE [%]")
    fig_pos_time.set_ylabel("Position RPE [m/s]")

    fig_rot_metric.set_ylabel("Rotation RPE [deg / 100m]")
    fig_rot_time.set_ylabel("Rotation RPE [deg/s]")

    fig_pos_metric.set_xlabel("pair distance [m]")
    fig_pos_time.set_xlabel("pair distance [s]")
    fig_rot_metric.set_xlabel("pair distance [m]")
    fig_rot_time.set_xlabel("pair distance [s]")

    figure_dict: dict[str, dict[PairDistanceUnit, Axes]] = {
        "pos": {
            PairDistanceUnit.METER: fig_pos_metric,
            PairDistanceUnit.SECOND: fig_pos_time,
        },
        "rot": {
            PairDistanceUnit.METER: fig_rot_metric,
            PairDistanceUnit.SECOND: fig_rot_time,
        },
    }

    plot_rpe_pos(figure_dict["pos"], rpe_results)
    plot_rpe_rot(figure_dict["rot"], rpe_results)

    add_rpy_legend(figure_dict)

    ret_sum = 1 if any(dev.rpe_dev.pair_distance_unit == PairDistanceUnit.METER for dev in rpe_results) else 0
    if any(dev.rpe_dev.pair_distance_unit == PairDistanceUnit.SECOND for dev in rpe_results):
        ret_sum += 2

    plt.close({1: fig_time, 2: fig_metric}.get(ret_sum))
    plt.tight_layout()
    return {
        0: (None, None),
        1: (fig_metric, None),
        2: (None, fig_time),
        3: (fig_metric, fig_time),
    }[ret_sum]

scatter_ate

scatter_ate(ate_result: ATEResult, plot_settings: MPLPlotSettings = MPLPlotSettings()) -> tuple[Figure, Figure]

Plots the ATE results as a scatter plot with color-coded deviations.

Parameters:

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def scatter_ate(ate_result: ATEResult, plot_settings: MPLPlotSettings = MPLPlotSettings()) -> tuple[Figure, Figure]:
    """
    Plots the ATE results as a scatter plot with color-coded deviations.

    Args:
        ate_result (ATEResult): ATE result to plot.
        plot_settings (MPLPlotSettings, optional): Plot settings. Defaults to MPLPlotSettings().
    """
    pos_fig = plt.figure()
    _colored_scatter_plot(
        xyz=ate_result.trajectory.xyz,
        c_list=ate_result.pos_dev_comb * plot_settings.unit_multiplier,
        c_label=f"Deviation {plot_settings.unit_str}",
        plot_settings=plot_settings,
    )

    if not ate_result.has_orientation:
        return pos_fig, None

    rot_fig = plt.figure()
    _colored_scatter_plot(
        xyz=ate_result.trajectory.xyz,
        c_list=ate_result.rot_dev_comb * 180 / np.pi,
        c_label="Deviation [°]",
        plot_settings=plot_settings,
    )
    return pos_fig, rot_fig

plot_positions

plot_positions(trajectories: list[Trajectory], scatter_3d: bool = False) -> Figure

Plots xy(z) coordinates of trajectories as 2d or 3d plot

Source code in trajectopy\visualization\mpl_plots.py
def plot_positions(trajectories: list[Trajectory], scatter_3d: bool = False) -> Figure:
    """Plots xy(z) coordinates of trajectories as 2d or 3d plot"""
    x_label, y_label, z_label = get_axis_label(trajectories=trajectories)

    if scatter_3d:
        fig_pos = plt.figure()
        ax_pos = fig_pos.add_subplot(111, projection="3d")
        ax_pos.set_zlabel(z_label)  # type: ignore
    else:
        fig_pos, ax_pos = plt.subplots()
        ax_pos.axis("equal")

    ax_pos.set_xlabel(x_label)
    ax_pos.set_ylabel(y_label)

    legend_names = []
    for traj in trajectories:
        xyz = traj.xyz
        legend_names.append(traj.name)

        # pos fig
        if scatter_3d:
            ax_pos.plot(xyz[:, 0], xyz[:, 1], xyz[:, 2])
        else:
            ax_pos.plot(xyz[:, 0], xyz[:, 1])

    if scatter_3d:
        set_aspect_equal_3d(ax_pos)

    fig_pos.legend(legend_names, ncol=4, loc="upper center")
    return fig_pos

plot_xyz

plot_xyz(trajectories: list[Trajectory], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure

Plots xyz coordinates of trajectories as subplots

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_xyz(trajectories: list[Trajectory], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure:
    """Plots xyz coordinates of trajectories as subplots"""
    fig_xyz, axs_xyz = plt.subplots(3, 1, sharex=True)

    for ax, label in zip(axs_xyz, get_axis_label(trajectories=trajectories)):
        ax.set_ylabel(label)

    trajectories_sorting = get_sorting([traj.sorting for traj in trajectories])
    all_unix = all(traj.is_unix_time for traj in trajectories)
    use_datetime_axis = _use_datetime_axis(trajectories_sorting, all_unix, plot_settings)
    x_label = (
        "relative time [s]"
        if plot_settings.use_relative_timestamps and trajectories_sorting == TrajectoriesSorting.ALL_TIME
        else derive_xlabel_from_sortings(trajectories_sorting, all_unix)
    )
    time_reference = min((traj.timestamps[0] for traj in trajectories if len(traj.timestamps) > 0), default=0.0)

    axs_xyz[-1].set_xlabel(x_label)

    if use_datetime_axis:
        axs_xyz[-1].xaxis.set_major_formatter(DATE_FORMATTER)

    legend_names = []
    min_x, max_x = None, None
    for traj in trajectories:
        legend_names.append(traj.name)
        xyz = traj.xyz

        # xyz fig
        for j, ax in enumerate(axs_xyz):
            if use_datetime_axis:
                index = traj.datetimes
            elif plot_settings.use_relative_timestamps and traj.sorting == Sorting.TIME:
                index = traj.timestamps - time_reference
            else:
                index = traj.index
            ax.plot(index, xyz[:, j])

            min_x = min(min_x, index[0]) if min_x is not None else index[0]
            max_x = max(max_x, index[-1]) if max_x is not None else index[-1]

    for ax in axs_xyz:
        ax.set_xlim(min_x, max_x)

    fig_xyz.legend(legend_names, ncol=4, loc="upper center")
    return fig_xyz

plot_rpy

plot_rpy(trajectories: list[Trajectory], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure | None

Plots rpy coordinates of trajectories as subplots

Source code in trajectopy\visualization\mpl_plots.py
@_with_mpl_rc
def plot_rpy(trajectories: list[Trajectory], plot_settings: MPLPlotSettings = MPLPlotSettings()) -> Figure | None:
    """Plots rpy coordinates of trajectories as subplots"""
    fig_rpy, axs_rpy = plt.subplots(3, 1, sharex=True)
    trajectories_sorting = get_sorting([traj.sorting for traj in trajectories])
    all_unix = all(traj.is_unix_time for traj in trajectories)
    use_datetime_axis = _use_datetime_axis(trajectories_sorting, all_unix, plot_settings)
    x_label = (
        "relative time [s]"
        if plot_settings.use_relative_timestamps and trajectories_sorting == TrajectoriesSorting.ALL_TIME
        else derive_xlabel_from_sortings(trajectories_sorting, all_unix)
    )
    time_reference = min((traj.timestamps[0] for traj in trajectories if len(traj.timestamps) > 0), default=0.0)

    axs_rpy[-1].set_xlabel(x_label)

    if use_datetime_axis:
        axs_rpy[-1].xaxis.set_major_formatter(DATE_FORMATTER)

    not_empty = False
    legend_names = []
    min_x, max_x = None, None
    for traj in trajectories:
        # rpy fig
        if traj.rotations and len(traj.rotations) > 0:
            legend_names.append(traj.name)
            rpy = traj.rpy
            ylabels = ["roll [°]", "pitch [°]", "yaw [°]"]
            for j, (ax, yl) in enumerate(zip(axs_rpy, ylabels)):
                if use_datetime_axis:
                    index = traj.datetimes
                elif plot_settings.use_relative_timestamps and traj.sorting == Sorting.TIME:
                    index = traj.timestamps - time_reference
                else:
                    index = traj.index
                ax.plot(index, np.rad2deg(rpy[:, j]))
                ax.set_ylabel(yl)
                ax.set_xlim(index[0], index[-1])
                min_x = min(min_x, index[0]) if min_x is not None else index[0]
                max_x = max(max_x, index[-1]) if max_x is not None else index[-1]
            not_empty = True

    for ax in axs_rpy:
        ax.set_xlim(min_x, max_x)

    fig_rpy.legend(legend_names, ncol=4, loc="upper center")

    return fig_rpy if not_empty else None