trajectopy 4.2.4__tar.gz → 4.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {trajectopy-4.2.4 → trajectopy-4.3.1}/PKG-INFO +1 -1
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_data/report_settings.json +2 -2
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/ate.py +2 -1
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_merging.py +9 -3
- trajectopy-4.3.1/trajectopy/__version__.py +1 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/core/settings.py +4 -2
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/core/trajectory.py +14 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/file_manager.py +2 -2
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/requests.py +0 -2
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/trajectory_manager.py +8 -80
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/utils.py +38 -16
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/json_settings_view.py +4 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/trajectory_context_menu.py +51 -283
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/data.py +1 -1
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/settings.md +8 -6
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/user_guide.md +2 -1
- trajectopy-4.2.4/trajectopy/__version__.py +0 -1
- {trajectopy-4.2.4 → trajectopy-4.3.1}/.gitattributes +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/.gitignore +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/.pre-commit-config.yaml +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/.pylintrc +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/CITATION.cff +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/LICENSE +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/README.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_data/KITTI_ORB.traj +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_data/KITTI_SPTAM.traj +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_data/KITTI_gt.traj +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_data/default.mplstyle +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_data/matplotlib_settings.json +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/align.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/alignment.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/mpl_plot.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/multi_report.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/rpe.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/single_report.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/example_scripts/trajectory_plot.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/pyproject.toml +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/requirements.txt +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/abs_dev.result +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/align_entry.result +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/generated_trajectory.traj +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/noisy_trajectory.traj +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/open_loop_trajectory.traj +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/rel_dev_meter.result +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/data/rel_dev_second.result +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_alignment.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_approximation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_comparison.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_io.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_matching.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_pipeline.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_positions_rotations.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_processing_edge_cases.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_processing_errors.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_readers_edge_cases.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_readers_errors.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_settings.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_settings_edge_cases.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_settings_validation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_sorting.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_trajectory.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/test_trajectory_validation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/testdata.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/test/util.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/LICENSE +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/__main__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/cli/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/cli/main.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/core/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/core/positions.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/core/rotations.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/exceptions.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/app.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/plot_manager.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/session_manager.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/managers/ui_manager.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/entries.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/property_model.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/result_model.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/selection.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/table_model.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/models/trajectory_model.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/resources/full-icon-poppins.png +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/resources/icon-bg.ico +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/resources/icon-bg.png +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/resources/icon.png +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/themes.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/about_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/alignment_edit_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/dof_organizer.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/main_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/mpl_plot_tabs.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/playback_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/progress_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/properties_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/result_context_menu.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/result_selection_window.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/result_table_view.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/timeline_widget.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/gui/views/trajectory_table_view.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/alignment.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/approximation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/evaluation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/interpolation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/direct.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/egrad_interface.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/equations.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/estimation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/parameters.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/cubic_approximation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/line_approximation.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/voxelizer.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/matching.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/merging.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/processing/sorting.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/py.typed +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/readers/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/readers/ascii.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/readers/rosbag.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/readers/rosmsg.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/readers/utils.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/results/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/results/alignment_result.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/results/ate_result.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/results/rpe_result.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/utils/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/utils/common.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/utils/definitions.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/__init__.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/assets/icon.png +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/assets/style.css +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/default.mplstyle +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/mpl_plots.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/plotly_plots.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/plotly_reports.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/report_data.py +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/templates/generic.html +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/templates/multi_template.html +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy/visualization/templates/single_template.html +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/positions.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/rotations.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/settings.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/trajectory.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/index.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/alignment.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/approximation.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/evaluation.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/interpolation.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/matching.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/merging.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/parameters.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/sorting.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/results/alignment_result.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/results/ate_result.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/results/rpe_result.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/visualization/mpl_plots.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/visualization/plotly_plots.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/api/visualization/plotly_reports.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/assets/icon.png +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/file_formats.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/index.md +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/javascripts/mathjax.js +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/docs/stylesheets/extra.css +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy-docs/mkdocs.yml +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/trajectopy.spec +0 -0
- {trajectopy-4.2.4 → trajectopy-4.3.1}/uv.lock +0 -0
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"scatter_smooth": false,
|
|
20
20
|
"scatter_smooth_window": 5,
|
|
21
21
|
"scatter_plot_on_map": false,
|
|
22
|
-
"scatter_mapbox_style": "
|
|
22
|
+
"scatter_mapbox_style": "carto-positron",
|
|
23
23
|
"scatter_mapbox_zoom": 15,
|
|
24
|
-
"scatter_mapbox_token": "
|
|
24
|
+
"scatter_mapbox_token": "",
|
|
25
25
|
"pos_x_name": "x",
|
|
26
26
|
"pos_y_name": "y",
|
|
27
27
|
"pos_z_name": "z",
|
|
@@ -28,7 +28,8 @@ def main():
|
|
|
28
28
|
# default settings
|
|
29
29
|
processing_settings = ProcessingSettings()
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
# align = True is the default, but can be disabled if you want to evaluate the unaligned trajectories
|
|
32
|
+
ate_result = ate(other=gt_traj, trajectory=est_traj, processing_settings=processing_settings, align=True)
|
|
32
33
|
console.print(dict_to_table(ate_result.property_dict))
|
|
33
34
|
|
|
34
35
|
|
|
@@ -59,7 +59,8 @@ class TestMerging(unittest.TestCase):
|
|
|
59
59
|
self.assertIsInstance(merged, Trajectory)
|
|
60
60
|
|
|
61
61
|
# Verify length is sum of input trajectories
|
|
62
|
-
|
|
62
|
+
unique_timestamps = np.unique(np.concatenate([self.traj1.timestamps, self.traj2.timestamps]))
|
|
63
|
+
expected_length = len(unique_timestamps)
|
|
63
64
|
self.assertEqual(len(merged), expected_length)
|
|
64
65
|
|
|
65
66
|
# Verify name
|
|
@@ -81,7 +82,10 @@ class TestMerging(unittest.TestCase):
|
|
|
81
82
|
merged = merge_trajectories([self.traj1, self.traj2, self.traj3])
|
|
82
83
|
|
|
83
84
|
# Verify all trajectories are included
|
|
84
|
-
|
|
85
|
+
unique_timestamps = np.unique(
|
|
86
|
+
np.concatenate([self.traj1.timestamps, self.traj2.timestamps, self.traj3.timestamps])
|
|
87
|
+
)
|
|
88
|
+
expected_length = len(unique_timestamps)
|
|
85
89
|
self.assertEqual(len(merged), expected_length)
|
|
86
90
|
|
|
87
91
|
# Timestamps should still be sorted
|
|
@@ -104,9 +108,11 @@ class TestMerging(unittest.TestCase):
|
|
|
104
108
|
|
|
105
109
|
merged = merge_trajectories([traj1_no_rot, traj2_no_rot])
|
|
106
110
|
|
|
111
|
+
unique_timestamps = np.unique(np.concatenate([traj1_no_rot.timestamps, traj2_no_rot.timestamps]))
|
|
112
|
+
|
|
107
113
|
# Should work without rotations (creates identity rotations)
|
|
108
114
|
self.assertIsNotNone(merged)
|
|
109
|
-
self.assertEqual(len(merged), len(
|
|
115
|
+
self.assertEqual(len(merged), len(unique_timestamps))
|
|
110
116
|
|
|
111
117
|
def test_merge_trajectories_preserves_epsg(self):
|
|
112
118
|
"""Test that EPSG code is preserved in merging."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.3.1"
|
|
@@ -543,6 +543,7 @@ class AlignmentSettings(Settings):
|
|
|
543
543
|
Combines preprocessing, estimation, and stochastic settings for trajectory alignment.
|
|
544
544
|
|
|
545
545
|
Attributes:
|
|
546
|
+
enabled: Whether alignment is enabled.
|
|
546
547
|
preprocessing: Preprocessing filter settings
|
|
547
548
|
estimation_settings: Parameter estimation settings
|
|
548
549
|
stochastics: Stochastic model (observation uncertainty) settings
|
|
@@ -552,6 +553,7 @@ class AlignmentSettings(Settings):
|
|
|
552
553
|
Defaults to 1e-4.
|
|
553
554
|
"""
|
|
554
555
|
|
|
556
|
+
enabled: bool = True
|
|
555
557
|
preprocessing: AlignmentPreprocessing = field(default_factory=AlignmentPreprocessing)
|
|
556
558
|
estimation_settings: AlignmentEstimationSettings = field(default_factory=AlignmentEstimationSettings)
|
|
557
559
|
stochastics: AlignmentStochastics = field(default_factory=AlignmentStochastics)
|
|
@@ -978,7 +980,7 @@ class ReportSettings(Settings):
|
|
|
978
980
|
scatter_smooth: Smooth scatter plot color data. Defaults to False.
|
|
979
981
|
scatter_smooth_window: Window size for scatter smoothing. Defaults to 5.
|
|
980
982
|
scatter_plot_on_map: Plot trajectories on mapbox map (requires valid EPSG). Defaults to False.
|
|
981
|
-
scatter_mapbox_style: Mapbox style (some require token). Defaults to "
|
|
983
|
+
scatter_mapbox_style: Mapbox style (some require token). Defaults to "carto-positron".
|
|
982
984
|
scatter_mapbox_zoom: Mapbox zoom level. Defaults to 15.
|
|
983
985
|
scatter_mapbox_token: Mapbox access token (optional). Defaults to "".
|
|
984
986
|
pos_x_name: Label for X position coordinate. Defaults to "x".
|
|
@@ -1024,7 +1026,7 @@ class ReportSettings(Settings):
|
|
|
1024
1026
|
scatter_smooth_window: int = 5
|
|
1025
1027
|
|
|
1026
1028
|
scatter_plot_on_map: bool = False
|
|
1027
|
-
scatter_mapbox_style: str = "
|
|
1029
|
+
scatter_mapbox_style: str = "carto-positron"
|
|
1028
1030
|
scatter_mapbox_zoom: int = 15
|
|
1029
1031
|
scatter_mapbox_token: str = ""
|
|
1030
1032
|
|
|
@@ -73,6 +73,20 @@ class Trajectory:
|
|
|
73
73
|
|
|
74
74
|
self.timestamps = np.arange(0, len(positions)) if timestamps is None else timestamps
|
|
75
75
|
|
|
76
|
+
# ensure strictly increasing timestamps
|
|
77
|
+
unique_timestamps, unique_indices = np.unique(self.timestamps, return_index=True)
|
|
78
|
+
|
|
79
|
+
if len(unique_timestamps) != len(self.timestamps):
|
|
80
|
+
logger.warning(
|
|
81
|
+
"Timestamps contain duplicates. Only the first occurrence of each unique timestamp will be kept. "
|
|
82
|
+
"This may lead to loss of data if duplicates are present."
|
|
83
|
+
)
|
|
84
|
+
self.timestamps = unique_timestamps
|
|
85
|
+
self.positions.xyz = self.positions.xyz[unique_indices, :]
|
|
86
|
+
|
|
87
|
+
if self.rotations is not None:
|
|
88
|
+
self.rotations = Rotations.from_quat(self.rotations.as_quat()[unique_indices, :])
|
|
89
|
+
|
|
76
90
|
if velocity_xyz is not None and len(velocity_xyz) == len(self.positions):
|
|
77
91
|
self._velocity_xyz = velocity_xyz
|
|
78
92
|
else:
|
|
@@ -61,9 +61,9 @@ class FileManager(QObject):
|
|
|
61
61
|
|
|
62
62
|
@show_progress
|
|
63
63
|
@Slot(FileRequest)
|
|
64
|
-
def handle_request(self, request: FileRequest)
|
|
64
|
+
def handle_request(self, request: FileRequest):
|
|
65
65
|
"""Logic for handling a request."""
|
|
66
|
-
self._request_executor.submit(generic_request_handler, self, request, True)
|
|
66
|
+
return self._request_executor.submit(generic_request_handler, self, request, True)
|
|
67
67
|
|
|
68
68
|
def shutdown_executor(self) -> None:
|
|
69
69
|
"""Best-effort cleanup for the persistent Python worker thread."""
|
|
@@ -48,15 +48,6 @@ class TrajectoryEntryPair:
|
|
|
48
48
|
request: TrajectoryManagerRequest
|
|
49
49
|
reference_entry: TrajectoryEntry | None = None
|
|
50
50
|
|
|
51
|
-
def __post_init__(self) -> None:
|
|
52
|
-
if self.request.type in [
|
|
53
|
-
TrajectoryManagerRequestType.ATE,
|
|
54
|
-
TrajectoryManagerRequestType.RPE,
|
|
55
|
-
]: # pipeline requests use matching setting from trajectory
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
self.entry.settings.matching.method = self.request.matching_method
|
|
59
|
-
|
|
60
51
|
|
|
61
52
|
class TrajectoryManager(QObject):
|
|
62
53
|
"""
|
|
@@ -144,16 +135,6 @@ class TrajectoryManager(QObject):
|
|
|
144
135
|
apply_to_reference=True,
|
|
145
136
|
),
|
|
146
137
|
TrajectoryManagerRequestType.AVERAGE: self.operation_average_trajectories,
|
|
147
|
-
TrajectoryManagerRequestType.COMPARE_ABS: lambda: self.handle_trajectory_operation(
|
|
148
|
-
operation=self.operation_compare_abs,
|
|
149
|
-
inplace=False,
|
|
150
|
-
apply_to_reference=False,
|
|
151
|
-
),
|
|
152
|
-
TrajectoryManagerRequestType.COMPARE_REL: lambda: self.handle_trajectory_operation(
|
|
153
|
-
operation=self.operation_compare_rel,
|
|
154
|
-
inplace=False,
|
|
155
|
-
apply_to_reference=False,
|
|
156
|
-
),
|
|
157
138
|
TrajectoryManagerRequestType.MERGE: self.operation_merge_trajectories,
|
|
158
139
|
TrajectoryManagerRequestType.SORT: lambda: self.handle_trajectory_operation(
|
|
159
140
|
operation=self.operation_sort, inplace=False, apply_to_reference=True
|
|
@@ -215,8 +196,9 @@ class TrajectoryManager(QObject):
|
|
|
215
196
|
None.
|
|
216
197
|
"""
|
|
217
198
|
self.request = request
|
|
218
|
-
self._request_executor.submit(generic_request_handler, self, request, False)
|
|
219
|
-
self.update_view.emit()
|
|
199
|
+
future = self._request_executor.submit(generic_request_handler, self, request, False)
|
|
200
|
+
future.add_done_callback(lambda ft: self.update_view.emit())
|
|
201
|
+
return future
|
|
220
202
|
|
|
221
203
|
def shutdown_executor(self) -> None:
|
|
222
204
|
"""Best-effort cleanup for the persistent Python worker thread."""
|
|
@@ -475,64 +457,6 @@ class TrajectoryManager(QObject):
|
|
|
475
457
|
|
|
476
458
|
self.emit_add_trajectory_signal(new_trajectory_entry)
|
|
477
459
|
|
|
478
|
-
@staticmethod
|
|
479
|
-
def operation_compare_abs(entry_pair: TrajectoryEntryPair) -> tuple[ResultEntry]:
|
|
480
|
-
"""
|
|
481
|
-
Compares the selected trajectory to the reference trajectory.
|
|
482
|
-
|
|
483
|
-
Args:
|
|
484
|
-
entry_pair (TrajectoryEntryPair): The pair of trajectories to compare.
|
|
485
|
-
|
|
486
|
-
Returns:
|
|
487
|
-
ResultEntry: The result of the comparison.
|
|
488
|
-
"""
|
|
489
|
-
if (reference_entry := entry_pair.reference_entry) is None:
|
|
490
|
-
raise ValueError("No reference trajectory selected.")
|
|
491
|
-
|
|
492
|
-
traj_test, traj_ref = matching.match_trajectories(
|
|
493
|
-
trajectory=entry_pair.entry.trajectory,
|
|
494
|
-
other=reference_entry.trajectory,
|
|
495
|
-
matching_settings=entry_pair.entry.settings.matching,
|
|
496
|
-
)
|
|
497
|
-
|
|
498
|
-
if len(traj_ref) != len(traj_test):
|
|
499
|
-
raise ValueError("Something went wrong during matching.")
|
|
500
|
-
|
|
501
|
-
comparison_result = evaluation._compare_trajectories_absolute(trajectory=traj_test, other=traj_ref)
|
|
502
|
-
|
|
503
|
-
return (AbsoluteDeviationEntry(deviations=comparison_result),)
|
|
504
|
-
|
|
505
|
-
@staticmethod
|
|
506
|
-
def operation_compare_rel(entry_pair: TrajectoryEntryPair) -> tuple[ResultEntry]:
|
|
507
|
-
"""
|
|
508
|
-
Compares the selected trajectory to the reference trajectory using relative comparison.
|
|
509
|
-
|
|
510
|
-
Args:
|
|
511
|
-
entry_pair (TrajectoryEntryPair): The pair of trajectories to compare.
|
|
512
|
-
|
|
513
|
-
Returns:
|
|
514
|
-
ResultEntry: The result of the comparison.
|
|
515
|
-
"""
|
|
516
|
-
if (reference_entry := entry_pair.reference_entry) is None:
|
|
517
|
-
raise ValueError("No reference trajectory selected.")
|
|
518
|
-
|
|
519
|
-
traj_test, traj_ref = matching.match_trajectories(
|
|
520
|
-
trajectory=entry_pair.entry.trajectory,
|
|
521
|
-
other=reference_entry.trajectory,
|
|
522
|
-
matching_settings=entry_pair.entry.settings.matching,
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
if len(traj_ref) != len(traj_test):
|
|
526
|
-
raise ValueError("Something went wrong during matching.")
|
|
527
|
-
|
|
528
|
-
comparison_result = evaluation._compare_trajectories_relative(
|
|
529
|
-
trajectory=traj_test,
|
|
530
|
-
other=traj_ref,
|
|
531
|
-
relative_comparison_settings=entry_pair.entry.settings.relative_comparison,
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
return (RelativeDeviationEntry(deviations=comparison_result),)
|
|
535
|
-
|
|
536
460
|
@staticmethod
|
|
537
461
|
def operation_interpolate_to_grid(
|
|
538
462
|
entry_pair: TrajectoryEntryPair,
|
|
@@ -824,7 +748,7 @@ class TrajectoryManager(QObject):
|
|
|
824
748
|
entry_pair (TrajectoryEntryPair): The pair of trajectories to compare.
|
|
825
749
|
|
|
826
750
|
Returns:
|
|
827
|
-
ResultEntry: The result of the comparison.
|
|
751
|
+
ResultEntry: The result of the comparison and the alignment information.
|
|
828
752
|
"""
|
|
829
753
|
if (reference_entry := entry_pair.reference_entry) is None:
|
|
830
754
|
raise ValueError("No reference trajectory selected.")
|
|
@@ -834,8 +758,12 @@ class TrajectoryManager(QObject):
|
|
|
834
758
|
other=reference_entry.trajectory,
|
|
835
759
|
processing_settings=entry_pair.entry.settings,
|
|
836
760
|
return_alignment=True,
|
|
761
|
+
align=entry_pair.entry.settings.alignment.enabled,
|
|
837
762
|
)
|
|
838
763
|
|
|
764
|
+
if not entry_pair.entry.settings.alignment.enabled:
|
|
765
|
+
return (AbsoluteDeviationEntry(deviations=ate_result),)
|
|
766
|
+
|
|
839
767
|
return (AbsoluteDeviationEntry(deviations=ate_result), AlignmentEntry(alignment_result=alignment_result))
|
|
840
768
|
|
|
841
769
|
@staticmethod
|
|
@@ -61,8 +61,6 @@ def handle_drag_move(event: QtGui.QDragMoveEvent | None) -> None:
|
|
|
61
61
|
def show_progress(func):
|
|
62
62
|
"""
|
|
63
63
|
Decorator to show progress bar while executing a function
|
|
64
|
-
|
|
65
|
-
This should be used for functions that take a long time to execute.
|
|
66
64
|
"""
|
|
67
65
|
|
|
68
66
|
@wraps(func)
|
|
@@ -71,12 +69,6 @@ def show_progress(func):
|
|
|
71
69
|
progress_window = getattr(manager, "_progress_window", None)
|
|
72
70
|
|
|
73
71
|
if progress_window is not None:
|
|
74
|
-
# Cross-thread: invoke the progress window slot directly via
|
|
75
|
-
# QMetaObject.invokeMethod with BlockingQueuedConnection.
|
|
76
|
-
# This guarantees the progress window is shown before work
|
|
77
|
-
# begins, even in PyInstaller-packaged executables where
|
|
78
|
-
# PySide6 signal dispatch across threads can be unreliable
|
|
79
|
-
# on the first call.
|
|
80
72
|
QMetaObject.invokeMethod(
|
|
81
73
|
progress_window,
|
|
82
74
|
"handle_show_request",
|
|
@@ -86,15 +78,45 @@ def show_progress(func):
|
|
|
86
78
|
manager.operation_started.emit()
|
|
87
79
|
|
|
88
80
|
try:
|
|
89
|
-
|
|
81
|
+
result = func(*args, **kwargs)
|
|
82
|
+
# Check if the result is a concurrent.futures.Future
|
|
83
|
+
import concurrent.futures
|
|
84
|
+
|
|
85
|
+
if isinstance(result, concurrent.futures.Future):
|
|
86
|
+
|
|
87
|
+
def on_done(future):
|
|
88
|
+
# We must emit signals from the manager's thread
|
|
89
|
+
# QMetaObject.invokeMethod handles this nicely if we call a slot,
|
|
90
|
+
# but we can also just emit the signal directly since Qt queued connections will route it.
|
|
91
|
+
manager.operation_finished.emit()
|
|
92
|
+
if progress_window is not None:
|
|
93
|
+
QMetaObject.invokeMethod(
|
|
94
|
+
progress_window,
|
|
95
|
+
"handle_close_request",
|
|
96
|
+
Qt.ConnectionType.BlockingQueuedConnection,
|
|
97
|
+
)
|
|
98
|
+
# If there's an exception, we might want to log it
|
|
99
|
+
try:
|
|
100
|
+
future.result()
|
|
101
|
+
except Exception as e:
|
|
102
|
+
import logging
|
|
103
|
+
|
|
104
|
+
logging.getLogger(__name__).exception("Error in background task: %s", e)
|
|
105
|
+
|
|
106
|
+
result.add_done_callback(on_done)
|
|
107
|
+
return result
|
|
108
|
+
else:
|
|
109
|
+
return result
|
|
90
110
|
finally:
|
|
91
|
-
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
# Only emit finished immediately if it's NOT a Future
|
|
112
|
+
if not isinstance(result, concurrent.futures.Future):
|
|
113
|
+
manager.operation_finished.emit()
|
|
114
|
+
if progress_window is not None:
|
|
115
|
+
QMetaObject.invokeMethod(
|
|
116
|
+
progress_window,
|
|
117
|
+
"handle_close_request",
|
|
118
|
+
Qt.ConnectionType.BlockingQueuedConnection,
|
|
119
|
+
)
|
|
98
120
|
|
|
99
121
|
return wrapper
|
|
100
122
|
|
|
@@ -214,6 +214,10 @@ class JSONViewer(QtWidgets.QMainWindow):
|
|
|
214
214
|
group_label = QtWidgets.QLabel(self.scroll_area_widget_contents)
|
|
215
215
|
group_label.setStyleSheet("color: #888888;")
|
|
216
216
|
|
|
217
|
+
if parent_name == "root":
|
|
218
|
+
# make first level keys more prominent
|
|
219
|
+
group_label.setStyleSheet("color: #888888; font-weight: bold; font-size: 14px;")
|
|
220
|
+
|
|
217
221
|
group_label.setObjectName(f"label-{parent_name}-{key}")
|
|
218
222
|
group_label.setText(_translate("MainWindow", format_key(key)))
|
|
219
223
|
self.form_layout.setWidget(self.form_item_cnt, QtWidgets.QFormLayout.ItemRole.LabelRole, group_label)
|