trajectopy 4.3.0__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.
Files changed (178) hide show
  1. {trajectopy-4.3.0 → trajectopy-4.3.1}/PKG-INFO +1 -1
  2. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_data/report_settings.json +2 -2
  3. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_merging.py +9 -3
  4. trajectopy-4.3.1/trajectopy/__version__.py +1 -0
  5. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/core/settings.py +2 -2
  6. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/core/trajectory.py +14 -0
  7. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/file_manager.py +2 -2
  8. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/trajectory_manager.py +3 -2
  9. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/utils.py +38 -16
  10. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/settings.md +2 -2
  11. trajectopy-4.3.0/trajectopy/__version__.py +0 -1
  12. {trajectopy-4.3.0 → trajectopy-4.3.1}/.gitattributes +0 -0
  13. {trajectopy-4.3.0 → trajectopy-4.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {trajectopy-4.3.0 → trajectopy-4.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {trajectopy-4.3.0 → trajectopy-4.3.1}/.gitignore +0 -0
  16. {trajectopy-4.3.0 → trajectopy-4.3.1}/.pre-commit-config.yaml +0 -0
  17. {trajectopy-4.3.0 → trajectopy-4.3.1}/.pylintrc +0 -0
  18. {trajectopy-4.3.0 → trajectopy-4.3.1}/CITATION.cff +0 -0
  19. {trajectopy-4.3.0 → trajectopy-4.3.1}/LICENSE +0 -0
  20. {trajectopy-4.3.0 → trajectopy-4.3.1}/README.md +0 -0
  21. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_data/KITTI_ORB.traj +0 -0
  22. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_data/KITTI_SPTAM.traj +0 -0
  23. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_data/KITTI_gt.traj +0 -0
  24. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_data/default.mplstyle +0 -0
  25. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_data/matplotlib_settings.json +0 -0
  26. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/align.py +0 -0
  27. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/alignment.py +0 -0
  28. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/ate.py +0 -0
  29. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/mpl_plot.py +0 -0
  30. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/multi_report.py +0 -0
  31. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/rpe.py +0 -0
  32. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/single_report.py +0 -0
  33. {trajectopy-4.3.0 → trajectopy-4.3.1}/example_scripts/trajectory_plot.py +0 -0
  34. {trajectopy-4.3.0 → trajectopy-4.3.1}/pyproject.toml +0 -0
  35. {trajectopy-4.3.0 → trajectopy-4.3.1}/requirements.txt +0 -0
  36. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/__init__.py +0 -0
  37. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/abs_dev.result +0 -0
  38. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/align_entry.result +0 -0
  39. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/generated_trajectory.traj +0 -0
  40. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/noisy_trajectory.traj +0 -0
  41. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/open_loop_trajectory.traj +0 -0
  42. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/rel_dev_meter.result +0 -0
  43. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/data/rel_dev_second.result +0 -0
  44. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_alignment.py +0 -0
  45. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_approximation.py +0 -0
  46. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_comparison.py +0 -0
  47. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_io.py +0 -0
  48. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_matching.py +0 -0
  49. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_pipeline.py +0 -0
  50. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_positions_rotations.py +0 -0
  51. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_processing_edge_cases.py +0 -0
  52. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_processing_errors.py +0 -0
  53. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_readers_edge_cases.py +0 -0
  54. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_readers_errors.py +0 -0
  55. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_settings.py +0 -0
  56. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_settings_edge_cases.py +0 -0
  57. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_settings_validation.py +0 -0
  58. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_sorting.py +0 -0
  59. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_trajectory.py +0 -0
  60. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/test_trajectory_validation.py +0 -0
  61. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/testdata.py +0 -0
  62. {trajectopy-4.3.0 → trajectopy-4.3.1}/test/util.py +0 -0
  63. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/LICENSE +0 -0
  64. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/__init__.py +0 -0
  65. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/__main__.py +0 -0
  66. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/cli/__init__.py +0 -0
  67. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/cli/main.py +0 -0
  68. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/core/__init__.py +0 -0
  69. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/core/positions.py +0 -0
  70. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/core/rotations.py +0 -0
  71. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/exceptions.py +0 -0
  72. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/__init__.py +0 -0
  73. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/app.py +0 -0
  74. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/__init__.py +0 -0
  75. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/plot_manager.py +0 -0
  76. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/requests.py +0 -0
  77. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/session_manager.py +0 -0
  78. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/managers/ui_manager.py +0 -0
  79. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/__init__.py +0 -0
  80. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/entries.py +0 -0
  81. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/property_model.py +0 -0
  82. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/result_model.py +0 -0
  83. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/selection.py +0 -0
  84. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/table_model.py +0 -0
  85. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/models/trajectory_model.py +0 -0
  86. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/resources/full-icon-poppins.png +0 -0
  87. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/resources/icon-bg.ico +0 -0
  88. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/resources/icon-bg.png +0 -0
  89. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/resources/icon.png +0 -0
  90. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/themes.py +0 -0
  91. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/__init__.py +0 -0
  92. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/about_window.py +0 -0
  93. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/alignment_edit_window.py +0 -0
  94. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/dof_organizer.py +0 -0
  95. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/json_settings_view.py +0 -0
  96. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/main_window.py +0 -0
  97. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/mpl_plot_tabs.py +0 -0
  98. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/playback_window.py +0 -0
  99. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/progress_window.py +0 -0
  100. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/properties_window.py +0 -0
  101. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/result_context_menu.py +0 -0
  102. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/result_selection_window.py +0 -0
  103. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/result_table_view.py +0 -0
  104. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/timeline_widget.py +0 -0
  105. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/trajectory_context_menu.py +0 -0
  106. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/gui/views/trajectory_table_view.py +0 -0
  107. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/__init__.py +0 -0
  108. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/alignment.py +0 -0
  109. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/approximation.py +0 -0
  110. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/evaluation.py +0 -0
  111. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/interpolation.py +0 -0
  112. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/__init__.py +0 -0
  113. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/__init__.py +0 -0
  114. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/data.py +0 -0
  115. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/direct.py +0 -0
  116. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/egrad_interface.py +0 -0
  117. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/equations.py +0 -0
  118. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/estimation.py +0 -0
  119. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/alignment/parameters.py +0 -0
  120. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/__init__.py +0 -0
  121. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/cubic_approximation.py +0 -0
  122. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/line_approximation.py +0 -0
  123. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/lib/approximation/voxelizer.py +0 -0
  124. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/matching.py +0 -0
  125. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/merging.py +0 -0
  126. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/processing/sorting.py +0 -0
  127. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/py.typed +0 -0
  128. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/readers/__init__.py +0 -0
  129. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/readers/ascii.py +0 -0
  130. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/readers/rosbag.py +0 -0
  131. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/readers/rosmsg.py +0 -0
  132. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/readers/utils.py +0 -0
  133. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/results/__init__.py +0 -0
  134. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/results/alignment_result.py +0 -0
  135. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/results/ate_result.py +0 -0
  136. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/results/rpe_result.py +0 -0
  137. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/utils/__init__.py +0 -0
  138. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/utils/common.py +0 -0
  139. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/utils/definitions.py +0 -0
  140. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/__init__.py +0 -0
  141. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/assets/icon.png +0 -0
  142. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/assets/style.css +0 -0
  143. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/default.mplstyle +0 -0
  144. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/mpl_plots.py +0 -0
  145. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/plotly_plots.py +0 -0
  146. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/plotly_reports.py +0 -0
  147. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/report_data.py +0 -0
  148. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/templates/generic.html +0 -0
  149. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/templates/multi_template.html +0 -0
  150. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy/visualization/templates/single_template.html +0 -0
  151. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/positions.md +0 -0
  152. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/rotations.md +0 -0
  153. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/settings.md +0 -0
  154. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/core/trajectory.md +0 -0
  155. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/index.md +0 -0
  156. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/alignment.md +0 -0
  157. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/approximation.md +0 -0
  158. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/evaluation.md +0 -0
  159. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/interpolation.md +0 -0
  160. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/matching.md +0 -0
  161. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/merging.md +0 -0
  162. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/parameters.md +0 -0
  163. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/processing/sorting.md +0 -0
  164. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/results/alignment_result.md +0 -0
  165. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/results/ate_result.md +0 -0
  166. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/results/rpe_result.md +0 -0
  167. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/visualization/mpl_plots.md +0 -0
  168. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/visualization/plotly_plots.md +0 -0
  169. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/api/visualization/plotly_reports.md +0 -0
  170. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/assets/icon.png +0 -0
  171. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/file_formats.md +0 -0
  172. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/index.md +0 -0
  173. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/javascripts/mathjax.js +0 -0
  174. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/stylesheets/extra.css +0 -0
  175. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/docs/user_guide.md +0 -0
  176. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy-docs/mkdocs.yml +0 -0
  177. {trajectopy-4.3.0 → trajectopy-4.3.1}/trajectopy.spec +0 -0
  178. {trajectopy-4.3.0 → trajectopy-4.3.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trajectopy
3
- Version: 4.3.0
3
+ Version: 4.3.1
4
4
  Summary: Trajectory Evaluation in Python
5
5
  Project-URL: Homepage, https://gereon-t.github.io/trajectopy/
6
6
  Project-URL: Documentation, https://gereon-t.github.io/trajectopy/
@@ -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": "open-street-map",
22
+ "scatter_mapbox_style": "carto-positron",
23
23
  "scatter_mapbox_zoom": 15,
24
- "scatter_mapbox_token": "pk.eyJ1IjoiZ2VyZW9uLXQiLCJhIjoiY2xwdGp6Nnh5MGZlejJqcnptemdlZnJicyJ9.fbQAv2C5pw7ctmzP_qX1pg",
24
+ "scatter_mapbox_token": "",
25
25
  "pos_x_name": "x",
26
26
  "pos_y_name": "y",
27
27
  "pos_z_name": "z",
@@ -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
- expected_length = len(self.traj1) + len(self.traj2)
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
- expected_length = len(self.traj1) + len(self.traj2) + len(self.traj3)
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(traj1_no_rot) + len(traj2_no_rot))
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"
@@ -980,7 +980,7 @@ class ReportSettings(Settings):
980
980
  scatter_smooth: Smooth scatter plot color data. Defaults to False.
981
981
  scatter_smooth_window: Window size for scatter smoothing. Defaults to 5.
982
982
  scatter_plot_on_map: Plot trajectories on mapbox map (requires valid EPSG). Defaults to False.
983
- scatter_mapbox_style: Mapbox style (some require token). Defaults to "open-street-map".
983
+ scatter_mapbox_style: Mapbox style (some require token). Defaults to "carto-positron".
984
984
  scatter_mapbox_zoom: Mapbox zoom level. Defaults to 15.
985
985
  scatter_mapbox_token: Mapbox access token (optional). Defaults to "".
986
986
  pos_x_name: Label for X position coordinate. Defaults to "x".
@@ -1026,7 +1026,7 @@ class ReportSettings(Settings):
1026
1026
  scatter_smooth_window: int = 5
1027
1027
 
1028
1028
  scatter_plot_on_map: bool = False
1029
- scatter_mapbox_style: str = "open-street-map"
1029
+ scatter_mapbox_style: str = "carto-positron"
1030
1030
  scatter_mapbox_zoom: int = 15
1031
1031
  scatter_mapbox_token: str = ""
1032
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) -> None:
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).result()
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."""
@@ -196,8 +196,9 @@ class TrajectoryManager(QObject):
196
196
  None.
197
197
  """
198
198
  self.request = request
199
- self._request_executor.submit(generic_request_handler, self, request, False).result()
200
- 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
201
202
 
202
203
  def shutdown_executor(self) -> None:
203
204
  """Best-effort cleanup for the persistent Python worker thread."""
@@ -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
- return func(*args, **kwargs)
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
- 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
- )
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
 
@@ -187,7 +187,7 @@ The ATE can be split into along-, horizontal-cross- and vertical-cross-track dir
187
187
  ### Mapbox Settings
188
188
 
189
189
  - `scatter_plot_on_map` (bool): Indicates whether the trajectory should be plotted onto a map. Default value is False.
190
- - `scatter_mapbox_style` (str): The style of the map. For some styles, a Mapbox token is required. Default value is "open-street-map".
190
+ - `scatter_mapbox_style` (str): The style of the map. For some styles, a Mapbox token is required. Default value is "carto-positron".
191
191
  - `scatter_mapbox_zoom` (int): The zoom level of the map. Default value is 15.
192
192
  - `scatter_mapbox_token` (str): The Mapbox token. Default value is "".
193
193
 
@@ -239,7 +239,7 @@ To plot trajectories on a map, several requirements must be met:
239
239
  - The trajectory must have valid EPSG information.
240
240
  - The plotting backend must be set to `plotly`.
241
241
  - The `scatter_plot_on_map` option must be enabled.
242
- - For `scatter_mapbox_style` other than `open-street-map`, a Mapbox access token (`scatter_mapbox_token`) must be provided. The mapbox token can be obtained after free registration at [Mapbox](https://www.mapbox.com/).
242
+ - For `scatter_mapbox_style` other than `carto-positron`, a Mapbox access token (`scatter_mapbox_token`) must be provided. The mapbox token can be obtained after free registration at [Mapbox](https://www.mapbox.com/).
243
243
 
244
244
  ## Matplotlib Plotting Settings (MPLPlotSettings)
245
245
 
@@ -1 +0,0 @@
1
- __version__ = "4.3.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes